Compare commits

...
Sign in to create a new pull request.

533 commits

Author SHA1 Message Date
a72553c2de config.py: support multiple base_url values via server_id 2025-05-05 19:06:46 +02:00
1d264fc54f allow usage of OIDC authentication (via py-scopes) where appropriate, and provide corresponding views in loops/server/auth.zcml 2025-05-05 09:58:38 +02:00
80c83d5c9f setUserId, query principal: fall back to virtual principal possibly provided by oidc 2025-05-04 22:40:44 +02:00
77fedaaeaa auth: remove obsolete definistions 2025-04-15 18:32:12 +02:00
636b209e9a work in progress: login with zitadel (Open ID Connect) 2025-04-06 22:33:16 +02:00
520c89f4b2 fix I18NValue: PersistentMapping.values() returns a generator, not a list 2025-03-31 17:23:45 +02:00
b6c93302b9 fix settings for loops and bluebream example instances 2025-03-29 16:05:22 +01:00
0340992932 fix sort values (Py3) 2025-03-09 17:12:37 +01:00
ed5e560ba4 fix sort concept relations (Py3): use '' instead of None 2025-03-09 11:26:48 +01:00
aa0443d0b5 psu: commit transaction when closing connection 2025-03-09 09:35:38 +01:00
0de7ef2550 Python3 fix: avoid None when sorting strings 2025-02-23 10:29:25 +01:00
bbc277e81d fix email handling: remove encode() 2025-02-11 15:59:56 +01:00
c3efe7a6f9 one more monkey patch for handling bytes array in text index 2025-02-03 11:11:29 +01:00
8d2f185d17 fix loops.repair: psu moved to loops.server 2025-01-11 11:46:04 +01:00
ec88df3405 remove old psu module; + minor Py3 fixes 2025-01-11 11:43:30 +01:00
678ed53ab5 add patches for ZODB.broken and zope.intid (provide _uid_int as object property) 2025-01-07 08:45:17 +01:00
72d7fdd05f add zope.sendmail patch (bug fix) 2024-12-31 15:07:08 +01:00
eddb58c794 fix email generation: remove 'encode()' 2024-12-31 14:44:12 +01:00
8a578b46a8 some more Python3 fixes 2024-12-30 09:52:02 +01:00
78c8b196bf loops instance settings: fixes and improvements 2024-12-18 19:15:08 +01:00
2b5a5ec65a add SERVER_ID to instance config 2024-12-18 11:33:23 +01:00
b8ce799b12 minor fix in organize.comment set up 2024-11-24 10:13:04 +01:00
c0fc7fd464 fix auth utility set-up in psu 2024-11-24 09:17:10 +01:00
e01a7362f9 work in progress: authentication: provide config parameter 2024-11-22 11:33:24 +01:00
74a3f9210b server.auth: register authentication utility 2024-11-16 09:31:33 +01:00
95ed826629 register JWT authentication utility defined in py-scopes (work in progress) 2024-11-15 08:43:22 +01:00
22efffa11b add monkey patch for zope.index; constraint zope.publisher to version 5.2.1 2024-10-26 19:21:23 +02:00
0f2232648c psu improvments for db and conn usage; fix table.py 2024-10-25 15:08:08 +02:00
14ea59a307 more Python3 fixes: remove basestring, unicode 2024-10-23 10:32:58 +02:00
8d7cda5ec0 fix psu.reindex_all(): additional keyword arguments, esp start 2024-10-22 22:27:46 +02:00
cfa079de0d fixes for server start functionality and inst examples 2024-10-06 11:24:00 +02:00
dc87e32a63 move psu from loops.common to loops.server; provide server.main for easy starting as server 2024-10-06 09:36:40 +02:00
2a689a871b fix overrides.zcml: set skin; fix resource indexing: avoid error when principal not found 2024-10-04 14:45:40 +02:00
76e189ac45 fix common overrides.zcml 2024-10-02 17:02:47 +02:00
0e180bb229 example instances: put all common stuff in loops package 2024-10-02 16:57:36 +02:00
98f023195b avoid testing deprecation errors with Python3.12 2024-10-02 12:54:34 +02:00
5dcc7d2fc6 more Python3 fixes and minor changes 2024-10-01 16:12:30 +02:00
2cf3569fcb loops site setup OK 2024-09-29 12:05:53 +02:00
6169a2d728 plain bluebream server basically working 2024-09-29 09:48:37 +02:00
ffa5c8701e work in progress: loops / bluebream instances (app installations) 2024-09-29 08:47:05 +02:00
48a51c54b1 use zope.testrunner for running all tests => additional Python3 fixes 2024-09-28 10:30:30 +02:00
74ce78dae9 removed xmlrpc package 2024-09-28 09:12:13 +02:00
f2a737e0a8 storage: tests with Python3 OK 2024-09-28 09:03:56 +02:00
6ae2590f50 Python3 fixes: constraint, media, system 2024-09-28 08:14:49 +02:00
1da71c72a9 more Python3 fixes: compound, knowledge, versioning 2024-09-27 09:37:54 +02:00
010106406d more Python3 fixes 2024-09-27 08:40:20 +02:00
992b5c012d classifier: Python3 fixes 2024-09-26 22:30:52 +02:00
bf1fda008c integrator: Python3 fixes 2024-09-26 19:59:51 +02:00
5e5e9aedfc organize (tracking, work): Python3 fixes 2024-09-26 16:25:00 +02:00
956a6d01b3 organize: Python3 fixes for: comment, job, stateful 2024-09-25 23:37:00 +02:00
52ac41a82f organize: more Python3 fixes 2024-09-25 18:49:21 +02:00
04a9d9ced0 Work in progress: Python3 fixes for loops.organize 2024-09-25 17:05:57 +02:00
509000a996 Python3 fixes for expert tests (+dependencies) 2024-09-25 10:58:27 +02:00
ce7015b224 Python3 fixes: standard tests OK 2024-09-25 09:30:37 +02:00
53be77b5b9 work in progress: Python3 fixes 2024-09-24 19:23:54 +02:00
42d24a5c3f Python3 fixes: test setup OK, starting with fixes in README.txt 2024-09-24 17:38:15 +02:00
494612235e more Python3 fixes: imports for setting up TestSite 2024-09-24 16:33:00 +02:00
ca70050bec more Python3 fixes: basic imports OK, starting with doctests 2024-09-24 15:30:46 +02:00
7a9be5d79b more Python3 fixes (browser, resource) 2024-09-24 11:47:59 +02:00
98bd9b0a46 work in progress: Python3 fixes 2024-09-24 11:13:49 +02:00
7b3c7c182e fix pyproject.toml - start testing / fixing with Python3 2024-09-23 17:41:38 +02:00
77aaad1aa0 start working on Python3-compatible version 2024-09-23 17:30:48 +02:00
4041841c65 storage.compat: provide __name__ property for tracks 2024-05-03 13:47:07 +02:00
cb994dc238 minor fix on util.records(): fail immediately if tracks folder is missing 2024-03-20 09:36:04 +01:00
baedf02d78 make compatibility method saveUserTrack() compatible with customized head fields arrangements 2024-03-19 08:59:46 +01:00
6efe2b7a46 remove obsolete import 2024-03-17 09:25:22 +01:00
d5a8068261 fix and improve setup of storage factory and storage 2024-03-10 08:04:18 +01:00
73ac0af54e fix storage creation: util.makeStorage() 2024-03-09 21:26:06 +01:00
3c82ec9fdc fix storage creation according to new implementation of storage setup with StorageFactory 2024-03-09 21:13:25 +01:00
81fef0e1d5 provide util.getStorage() functionality 2024-02-26 12:49:29 +01:00
31886012ce fix handling of migrated UIDs, add test 2024-02-19 11:02:00 +01:00
086868d5b7 improve migration: use list of ids for access to source collection 2024-02-19 09:13:25 +01:00
f51f3d4f25 migration: improve uid mapping by using separate columns for prefix and id 2024-02-19 08:55:54 +01:00
536903f3d8 improve / fix tracking migration 2024-02-19 08:33:50 +01:00
75fdced678 tests: check for correct DB engine settings 2024-02-15 11:27:03 +01:00
a739aed66a use new py-scopes package instead of cco.storage 2024-02-15 10:45:58 +01:00
92bc00e134 ensure correct initialization of db engine for testing 2024-02-09 16:44:24 +01:00
fb9d6991cd getItem(): use uid_mapping for legacy UIDs when appropriate 2024-02-07 17:53:27 +01:00
bea7ed0254 migration: allow source list, source list slicing, commit after step records 2024-02-06 17:26:08 +01:00
bece8863ef use now module-global engine and session factory 2024-02-01 15:21:36 +01:00
16a8bcf5c3 provide table for UID mappings, fill during migration 2024-02-01 11:43:37 +01:00
2dbbf977d4 storage test: drop table; reset trackid sequence after migration 2024-01-25 09:16:51 +01:00
23623aeb3d provide compat.Storage subclass; minor immprovements 2024-01-19 08:48:22 +01:00
70a93f56d9 move storage-related stuff from organize/tracking/storage to loops.storage package 2024-01-17 11:01:34 +01:00
ec87bcd682 fixes according to new cco.storage version 2024-01-17 10:28:58 +01:00
3d3e221a5c storage test: set correct db password 2024-01-05 18:50:04 +01:00
4635a00caf fix portlet selection; simplify options retrieval 2024-01-03 17:33:21 +01:00
64ad40fb96 clean-up source files 2024-01-03 16:57:47 +01:00
b4b93b122e move access to records container (in ZODB or SQL DB) to util.records() function 2024-01-03 15:44:08 +01:00
5c4b7fd730 use new IUid interface for showing the presence of the uid property 2024-01-03 13:00:36 +01:00
380d7e7b59 use SQL-based storage (cco.storage) for favorites 2024-01-03 11:09:50 +01:00
1cd9908afe RecordsTable: allow sorting of rows via type option 'table.sort' 2023-12-29 13:56:51 +01:00
6994d923a2 use new RecordsTable type for configurable records table 2023-12-29 13:26:46 +01:00
997bffc286 add IRecordsTable to interfaces list 2023-12-27 10:52:08 +01:00
81aa9d4e1e add RecordsTable to configure.zcml 2023-12-27 10:50:31 +01:00
45d568b77b assign widget to IRecordsTable definition 2023-12-27 10:40:41 +01:00
4343dc517f add RecordsTable type implemented as a simple list of records 2023-12-26 22:09:12 +01:00
a2ccb8a35b remove/hide print statements for clean tests 2023-12-26 22:08:16 +01:00
8e81bab3bf access to SQL-based implementation of favorites record basically working 2023-12-20 15:39:10 +01:00
466044eb77 work in progress: tests for adding and querying favorites 2023-12-19 15:31:35 +01:00
a65c138949 favorites: tests: minor improvements 2023-12-18 20:02:07 +01:00
2c22df9c1d test for migration of favorites basically working 2023-12-18 15:10:06 +01:00
506e539c2d work in progress: autommatic tests for SQL-based storage implementation 2023-12-18 13:53:37 +01:00
781521ca88 move storage migration from cybertools to loops, + updates from cco.storage 2023-12-15 14:13:47 +01:00
6d39f9f354 recreate (obsolete) QualificationRecord stuff to avoid crashing of records listing on old ZODBs 2023-12-08 11:54:20 +01:00
e3359db4ca avoid security check when retrieving skin name from loops root 2022-10-09 22:29:21 +02:00
c9c537af23 fix: add missing import 2021-12-06 15:56:29 +01:00
Hannes Plattner
1cf6bd3c06 allow adapterindexattributes title getter 2021-11-12 10:42:35 +01:00
zope
d4edd8ee7a add parameter usePredicateIndex to getRelations() 2021-08-07 11:53:04 +02:00
Hannes Plattner
4b54cadcac mark new object form adapter as __dummy__ 2021-07-09 11:34:45 +02:00
8e94c5971e fix doctests for loops.external (resources contentType) 2021-06-11 10:23:19 +02:00
Hannes Plattner
aab2d6c955 check if principal exists before adding to group 2021-06-11 10:02:13 +02:00
Hannes Plattner
34c9d24ffa add target blank to resource download button 2021-05-27 11:57:28 +02:00
1504375a25 add CSS classes to book display elements 2021-03-14 09:22:39 +01:00
346d041be4 remove upper limit for data field 2021-01-06 11:09:02 +01:00
4b4a804cb7 change upper limit for data field 2021-01-06 10:40:19 +01:00
2ddf3e03e7 add (dummy) parameter to 'getColumnRenderer()' 2020-12-14 08:43:23 +01:00
fecac89a84 fix breadcrumb check: view may be None because of restricted permissions 2020-08-18 10:23:15 +02:00
4f710c15d6 fix favorite display 2020-05-06 08:23:28 +02:00
522e40f242 fix import: stop deprecation warning 2020-05-04 16:30:34 +02:00
61a146a43d suppress checking for options in security check 2020-05-01 08:17:41 +02:00
31b81d10e3 re-activate redirect on state change 2020-04-29 10:54:01 +02:00
347e19a2c6 unquote form value only if string 2020-04-29 10:43:31 +02:00
ebcbab1a6c fix DateField (missing return) 2020-04-25 12:00:40 +02:00
ebacd14134 re-implement missing multiline field method 2020-04-15 10:40:49 +02:00
d07f3b85a9 don't suppress additional excess values in table rows 2020-04-08 14:51:50 +02:00
9d9cfd6fc7 Merge branch '2master' of ssh://git.cy55.de/home/git/loops into 2master 2020-03-23 11:23:24 +01:00
dd23787d27 avoid error when list value is None 2020-03-23 11:20:45 +01:00
0cd2e1f47d fix doctests, avoid deprecation warnings 2020-03-07 13:31:29 +01:00
8723289204 set version number to 2.3.0 2020-03-07 12:51:06 +01:00
b471deef43 merge bbmaster2 into bbmaster resulting in branch 2master 2020-03-04 18:05:37 +01:00
3263672934 extend/improve paster shell utilities 2020-03-04 16:55:53 +01:00
c3079472de fix typo and doctest after JSON fix 2019-12-06 07:56:30 +01:00
hplattner
b59248ee31 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2019-12-05 20:48:10 +01:00
hplattner
1b0f0e4859 add concept search filtering select fix 2019-12-05 20:47:57 +01:00
082acf3fbc add loopsRoot to module global variables 2019-12-01 16:22:40 +01:00
9ab3e61f9a make loopsRoot path and config module configurable in setup() function 2019-12-01 16:05:21 +01:00
1854903d0f conditional commit for deletion of objects 2019-12-01 11:31:52 +01:00
b4a8ae1ed1 some more improvements 2019-12-01 10:12:00 +01:00
c5fe2044b5 add common psu utilities, esp generic loop 2019-12-01 09:58:24 +01:00
9c2517a518 take psu.py from bbmaster2 branch, with some improvements 2019-12-01 09:42:45 +01:00
dc5d913b99 user account: add principal title if given 2019-11-28 08:33:24 +01:00
814f10ae51 unify naming: concept_macros means macros, not template 2019-11-27 13:20:42 +01:00
dae9610e2e allow hiding title, e.g. if shown already in another macro 2019-11-27 13:19:17 +01:00
7567374e50 fix page templates: avoid errors with new ZPT version 2019-04-28 16:55:49 +02:00
2012181382 fix tests/doctests according to current ZTK and BlueBream versions 2019-04-26 17:13:59 +02:00
e1b1a82ee1 allow suppressing of ObjectModifiedEvent (if done later by caller) 2019-04-26 10:33:33 +02:00
233d146587 remove obslete ugly hack from loops container 2019-04-10 08:23:19 +02:00
bbd1d06792 update version number 2019-03-09 13:26:55 +01:00
8bd69b12fd use targetItem to access also directly assigned concepts or resources 2018-10-17 08:17:52 +02:00
aa263eb635 avoid error because of error in title field of view 2018-08-03 15:07:12 +02:00
fb94aacaa8 add method for checking for top-level page (e.g. data protection stmt) 2018-05-19 11:24:29 +02:00
ff0e19a72c remove obsolete code 2018-05-19 11:23:42 +02:00
hplattner
66b616e5e1 add subviewmode feature 2018-05-16 12:04:14 +02:00
937f2ee8b0 work statement report: add selection field for person/party 2018-05-14 12:42:30 +02:00
afd4416eaa fix typos in JavaScript file 2018-05-04 16:13:42 +02:00
a296370484 when rendering .odt/.xlsx: use field names instead of title 2018-03-04 09:51:22 +01:00
4b5680d969 fix (after using dict instead of jeep): sort glossary correctly 2018-02-21 16:03:04 +01:00
590cffaa35 sort letters in headline 2018-02-19 14:37:10 +01:00
d282e3c93a avoid unicode error if keyword starts with special characters.
In addition show only starting characters appearing in word list
in headline.
2018-02-19 14:30:53 +01:00
e35fafad81 activate calling of script; remove print statements 2018-01-07 11:17:40 +01:00
de43ab03ca take file names/paths from appropriate settings 2017-12-31 15:56:05 +01:00
a13784e93e use markdown import and content type only when available 2017-12-29 10:37:13 +01:00
8c09a0e73d add tab title for book_topic_view; rearrange section methods 2017-12-08 12:17:02 +01:00
4b49fda269 make sure we always get numeric values 2017-12-07 18:26:27 +01:00
8c29a3e7d4 duration field: use 0 as default value; allow specification of a factor 2017-12-07 12:01:45 +01:00
3b37723cd4 show text 'more...' only if there is really (probably) more to see 2017-12-05 13:23:20 +01:00
cfc32d7ebd fix breadcrumbs for book topic view 2017-12-05 12:59:21 +01:00
ef8a47c1a1 column headers: use default translation domain if appropriate 2017-11-14 22:42:08 +01:00
516eda33d1 allow conversion/processing of CSV file to XLSX upon download 2017-11-14 17:41:56 +01:00
georgm
7f7a2af25f add markdown support 2017-10-14 16:04:53 +02:00
7dcf4a9f4e unit tests: move creation of externalIdentifier index to general setup 2017-10-13 11:07:40 +02:00
181846b29a fix indexing stuff (externalIdentifier) 2017-10-08 12:56:31 +02:00
d863305b94 fix check for adapter in 'externalIdentifier' 2017-10-08 12:44:04 +02:00
6a685187d5 fix import file (double 'options' parameter for 'topic' type) 2017-10-08 09:00:23 +02:00
6ca85b21db add option for creating and editing topics to topic type 2017-10-01 08:33:49 +02:00
6fd47572f2 control editing of work items via global option (persmission setting) 2017-08-29 14:06:21 +02:00
37d508cec2 remove obsolete 'jocy' registration 2017-08-20 15:55:33 +02:00
f8bca9e7d6 add utility functions for reindexing of objects 2017-08-10 10:37:25 +02:00
7a3cfbc175 avoid error when no groups folder is found (e.g. in doctests) 2017-08-02 08:53:12 +02:00
63220a656f always show all unread messages 2017-07-20 11:16:48 +02:00
hplattner
fb924fdc03 add mail image to notifications portlet 2017-07-20 10:48:56 +02:00
dc75d56951 process optional selection criteria (dateFrom) for notifications list 2017-07-19 15:41:49 +02:00
5d02a5ee03 fix URL for state change via portlet 2017-07-16 16:33:42 +02:00
536f204a03 use empty workitem_dayfrom_default option for empty start date 2017-05-09 12:07:37 +02:00
8f12304074 node traverser: add logging for NotFound exception 2017-04-10 11:47:17 +02:00
efb2c8ba71 fix GET params quoting on input fields 2017-03-13 10:43:35 +01:00
34926dde8c fix acces to view properties; optional use global copyright option 2017-01-12 11:34:15 +01:00
b7c702f17d add node type raw to be able to emit e.g. robots.txt and similar stuff 2017-01-12 10:28:06 +01:00
b3677ea635 fix initid monkey patch 2017-01-12 10:15:31 +01:00
421c21cdec make ZTK-compatible package that is installable as a Python egg 2017-01-02 16:06:14 +01:00
e51ebdb28c de-activate (obsolete) role for Flash (XML-RPC) concept map editor 2017-01-01 17:27:56 +01:00
4cfdebec77 fix language negotiation: use language defined via options 2017-01-01 17:26:47 +01:00
dd96f01a32 improve/fix settings for import of basic loops site structure 2017-01-01 17:25:41 +01:00
5083b2edc2 dirty fix: monkey patch in IntIds to avoid ForbiddenAttribute error 2017-01-01 17:24:00 +01:00
9670ace28c remove obsolete property from Loops root, ensuring compatibility with ZTK 2016-12-26 18:19:28 +01:00
96e7c20a56 remove deprecated import from zope.testing 2016-12-26 10:46:22 +01:00
a91e1b0c4c remove deprecated import from zope.testing 2016-12-26 10:37:00 +01:00
30c13d57d7 use special property 'favTitle' for favorites portlet 2016-12-19 14:45:09 +01:00
3115b4f19e fix description field in recent changes report 2016-11-18 12:48:13 +01:00
6261831340 indexing: gracefully fall back to ISO8859 if UTF-8 gives an error 2016-10-26 16:20:44 +02:00
f9ba9d2115 make sure virtualTargetObject is not adapted 2016-10-12 08:55:21 +02:00
4339f25ca3 backport from bbmaster2: check 'visible' property in display views 2016-10-08 14:14:17 +02:00
5f78698e06 check for 'visible' field property 2016-10-08 11:18:45 +02:00
7b0fe5aaf2 object creation: take type options from object to be created 2016-10-04 15:12:50 +02:00
ccc9886113 object creation: take type options from object to be created 2016-10-04 15:12:00 +02:00
hplattner
e27f4000f7 add uid to notifications listing items 2016-08-30 12:47:36 +02:00
5ffb1edfbc hide breadcrumbs in print 2016-08-30 08:19:07 +02:00
hplattner
214fb2dc0f Merge branch 'bbmaster2' of http://git.cy55.de/loops into bbmaster2 2016-08-25 12:17:21 +02:00
hplattner
df3ae0179b add canonicalURl to Layout TextView 2016-08-25 12:17:14 +02:00
hplattner
c64f067d6e Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2016-08-11 17:01:28 +02:00
hplattner
fbacdd7465 add structure for target report column renderer 2016-08-11 17:01:20 +02:00
0dc20ea52f allow more control for NodeView subclasses, e.g. login view 2016-07-19 12:17:32 +02:00
3b11a72a81 avoid loading all subchildren in check for nested children 2016-07-17 10:13:21 +02:00
0b5c50c100 avoid loading of all subchildren in check for nested listing 2016-07-17 09:58:05 +02:00
2720b0444c use workspace assignment also for 'create form' action 2016-07-14 09:52:25 +02:00
hplattner
040997b322 use options to determin x robots tag 2016-07-13 16:06:58 +02:00
hplattner
4fb3fa54ec add x robots tag to resources 2016-07-13 15:02:17 +02:00
949a543aa9 set state of survey data when saving 2016-06-28 08:14:58 +02:00
597a8d48ef add missing definition of requestUrl property 2016-06-24 07:56:57 +02:00
65ad427413 use absolute URLs for login and selfservice registration 2016-06-16 15:38:03 +02:00
9bf69e88b3 avoid duplication of data if context is cyclically assigned to itself 2016-06-16 13:51:20 +02:00
7cb0cf0d95 re-activate 'request/URL' on management page 2016-06-12 09:03:28 +02:00
d4e22b7f79 revert request/URL stuff for management views 2016-06-09 12:20:44 +02:00
9e9390cff3 use new 'view/requestUrl' to avoid '/@@index.html' in URLs 2016-06-09 10:22:30 +02:00
a8f70fd498 move new 'view/requestUrl' to cybertools/browser 2016-06-09 09:01:20 +02:00
17eafd7895 provide 'view/requestUrl' method to be used instead of 'request/URL' 2016-06-09 08:30:19 +02:00
97e7537c4c link consistency: limit URL tweaking to 'page' nodes 2016-06-08 10:34:28 +02:00
4ddddc0933 link consistency: take URL from menu item if target is assigned to one 2016-06-07 20:42:41 +02:00
01ab785c0d include 'done' work items in plan report 2016-06-05 15:59:22 +02:00
85dfd815dc make sure indirectly called reports (like CSV export) use the correct report name 2016-05-13 08:20:22 +02:00
428c772ea9 provide new report for planned work 2016-05-12 15:44:09 +02:00
6c21581c7e fix loop() function; make root folder globally accessible 2016-05-05 08:16:46 +02:00
b14e59e29c fix type 2016-05-02 09:53:12 +02:00
fa1170444d add paster shell utilities and repair base modules 2016-05-02 09:45:56 +02:00
e27556b98b provide additional helper function to get really all question groups 2016-04-28 10:37:27 +02:00
6c6c0791fa update existing survey data without overwriting them 2016-04-25 18:38:47 +02:00
dda87b0e79 profide method for loading a range of response tracks by wildcard query 2016-04-25 12:28:29 +02:00
b0139ccb13 Use target URL for tabs (better than request.URL) 2016-04-25 09:37:19 +02:00
89863c9fde avoid errors when directly opening report concept 2016-04-24 17:22:28 +02:00
22d1d44560 dynamically create tabs on concept views via options 2016-04-24 10:49:58 +02:00
4a18c42283 fix datatable source list; add generic IOptions adapter 2016-04-22 11:17:45 +02:00
665c0271fd (1) provide ILocation adapter for WSI; (2) activate security handlers.
(1) ILocation adapter seems to be required by absolute_url in
BlueBream.

(2) security-related event handlers had been commented out in
bbmaster for performance reasons; are now re-activated.
2016-04-17 14:05:59 +02:00
f12361daa4 delegate action: set date, start to current, clear other fields 2016-04-17 10:00:18 +02:00
635a4e2568 show candidates list for task only if there are any 2016-04-16 08:57:23 +02:00
8e9b4267f2 remove obsolete security declaration 2016-04-13 19:49:14 +02:00
1d84f5e509 use correct base interface for avoiding ForbiddenAttribute errors 2016-04-13 19:43:31 +02:00
8eab09cd44 revert previous change (was other error in cyberapps.knowledge) 2016-04-07 09:36:22 +02:00
f5731c1e3f avoid error because of deleted data 2016-04-07 09:17:14 +02:00
5f8ed47165 add person-based questionnaire
control team- and person-based questionnaires via questionnaireType;
person-based questionnaire: refer to (answer questions for) other person.
2016-04-07 08:24:27 +02:00
5b2b28da19 CSV export: store duration/effort as minutes 2016-03-29 18:17:44 +02:00
608b75f1c8 table-based vocabularies: avoid error when used directly with a Concept as instance 2016-03-29 18:09:06 +02:00
689bd983bc define default headTitle in BaseView 2016-03-28 11:37:18 +02:00
acef9b683e gracefully keep favorites on top that have been promoted manually 2016-03-24 17:11:52 +01:00
1d7f01dccb allow re-ordering of favorites by drag-and-drop 2016-03-24 16:24:45 +01:00
841eb204eb edit work item: reset times on work, finish 2016-03-24 16:24:21 +01:00
17f15429c8 add activity field to report, is also used as query field with vocabulary 2016-03-18 15:57:26 +01:00
354dfeda7f Merge branch 'master' into bbmaster2 2016-03-17 16:15:23 +01:00
db4f7d15a9 move JS generation to Python; reset date/time when selecting 'finish' in state 'done' 2016-03-17 15:24:09 +01:00
b87dee5c35 workstatement report: the default for start date (day from) can now be set via type option 'workitem_dayfrom_default' 2016-02-21 11:52:29 +01:00
711488a412 populate dayFrom field with a sensible default value; use form action for providing CSV export in order respect query input 2016-02-13 22:19:57 +01:00
0915d04e30 log 'Unauthorized' warning 2016-02-10 09:32:54 +01:00
24cd81e267 backport changes from bbmaster2 2016-02-04 15:45:33 +01:00
2e096a3682 backport changes from bbmaster2 2016-02-04 15:07:20 +01:00
f4a5b78700 CSV export: format duration and effort fields correctly 2016-01-26 13:32:04 +01:00
7d979a5749 make default work item action configurable via option 'organize.work.default_actions' 2016-01-24 11:04:10 +01:00
396d4727dc provide a collection of sub-parts of a page that may be placed separately on the page 2016-01-17 14:16:24 +01:00
f69a43699c avoid error for notifications whose target has been deleted 2016-01-15 11:59:18 +01:00
cd65a3f7b5 improve reporting CSS, person work statement report 2016-01-10 18:23:04 +01:00
e272675498 new report 'person work statement', + minor improvements for generic concept-based reports 2016-01-09 17:34:47 +01:00
4051c2f9a5 fix report template 2016-01-09 12:03:02 +01:00
b05c9c5851 allow setting of main template via controller (which is found via skin) 2016-01-09 12:02:14 +01:00
970775f847 make body template configurable: additional slots, variables controlling layout (CSS classes) 2015-12-06 12:43:34 +01:00
8080342620 don't try to get a view for an object the user does not have access to 2015-12-01 20:47:26 +01:00
f1b7e01cbc avoid error because of missing renderer macro 2015-11-25 11:17:30 +01:00
hplattner
1f4d246994 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2015-11-19 14:18:58 +01:00
hplattner
fbf4efc40c add assign/ deassignParent wrapper for resources; add adapterIndexAttributes + keywords index adapter lookup for resources 2015-11-19 14:18:48 +01:00
bd33c28d08 move login form (NodeView) to cco.member; + helper methods in NodeView 2015-11-13 16:25:34 +01:00
babb222868 starting to move authentication to cco.member package; allow special characters in logout's nextUrl 2015-11-12 15:45:29 +01:00
hplattner
9ec755ecd3 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2015-11-06 08:44:53 +01:00
a3b649582c add states definition for person 2015-11-04 14:59:35 +01:00
0b4fdef7b5 show link to notifications page also for 'no new notifications' 2015-11-04 09:27:50 +01:00
hplattner
16b5665bfa send report params form with get request 2015-11-03 17:59:12 +01:00
357c660659 notification: avoid error because of user without person 2015-11-03 17:26:34 +01:00
5c90a19859 notification: minor fixes 2015-10-31 10:56:09 +01:00
a2dca10e15 show portlet only if there are any notifications 2015-10-31 10:05:13 +01:00
ec28357ba7 notifications: portlet, translations 2015-10-30 12:52:42 +01:00
4a0b31b34d notifications listing fully operative with marking notifications as read and filtering 2015-10-30 10:50:05 +01:00
d09e2a9d0a work in progress: notifications listing 2015-10-25 15:42:36 +01:00
3990b710c6 avoid prindipal IDs with special characters, skipping existing ones if necessary 2015-10-25 10:00:26 +01:00
019eef29a6 work in progress: notification listing; improve 'unauthorized' view 2015-10-23 10:36:46 +02:00
c029cb2356 provide login.html as concept view (for use with a query), + a similar unauthorized view 2015-10-19 16:26:41 +02:00
e929a3154e avoid UnicodeEncodeError when object name contains special characters 2015-10-19 09:31:34 +02:00
57588f1e19 fix access to manage_workspace page 2015-10-19 09:30:23 +02:00
853782fb35 work in progress: notifications listing 2015-10-17 12:17:56 +02:00
9f9df6599f set up basic notification functionality 2015-10-16 09:26:49 +02:00
1800fe7c9e merge branch master 2015-10-10 11:48:52 +02:00
2c512a1ff7 make indexing more fault-tolerant 2015-10-10 11:46:54 +02:00
f64b60f3b5 Merge branch 'bbmaster' into bbmaster2 2015-08-29 11:13:05 +02:00
07bb68ae9d merge branch master 2015-08-29 11:12:58 +02:00
2ae8a60b18 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2015-08-29 11:08:21 +02:00
4229e24ff8 fix typo (additional recipients) 2015-08-04 10:42:16 +02:00
9587a86f32 avoid error on catalog reindex because of missing or corrupt ZIP files 2015-07-27 10:17:28 +02:00
64c59e76a9 add *.pyo to .gitignore 2015-07-10 09:10:01 +02:00
e56b0c9863 show views for loops container and view manager only for site managers 2015-06-23 14:52:47 +02:00
d445239beb backport fixes (member creation) from bbmaster2 branch 2015-06-17 16:29:43 +02:00
14242e77c7 avoid error when no report assigned 2015-06-17 16:28:44 +02:00
3c1a5ccdf4 merge branch master 2015-06-12 08:35:07 +02:00
5bf2906c51 avoid error when no report is found 2015-06-12 07:20:01 +02:00
6b09ed2f9f use setting on type for getting container for object creation 2015-04-16 12:31:31 +02:00
1efa3cbbdb execute state transition before setting any fields to avoid side effects by settings in fields 2014-11-10 10:30:20 +01:00
ff23ddf90a always track state changes; allow control of change tracking via 'force' parameter 2014-11-03 10:32:51 +01:00
13f80f2093 media asset: take modification date from file for versions 2014-10-02 07:57:38 +02:00
e4df135f5c put query fields side by side; work in progress: field groups 2014-09-16 15:41:42 +02:00
d44b388b67 relation query field: show field data entered upon report execution 2014-09-16 13:57:28 +02:00
8fff0330d3 provide variant of report view that shows results only after query parameters have been entered 2014-09-15 19:36:50 +02:00
acbb1aa944 move flag for hiding states portlet to view/request annotation 2014-08-12 13:42:06 +02:00
68af0010aa allow suppression of states portlet via flag 'hideStatesPortlet' in view 2014-08-12 13:13:22 +02:00
d07daca284 use new merge method for controlling report macros; provide default macros for some report field types 2014-08-12 13:08:35 +02:00
c28d2e88f1 do not show versioning part in edit form for concepts 2014-06-14 09:47:22 +02:00
da24ef6324 minor fixes on versioning, probably due to changes in Python 2.7 and BlueBream 2014-06-03 11:09:35 +02:00
ca8f509748 use title of adapted target object 2014-06-03 11:08:44 +02:00
65a861ca24 adjust line to max line lenght according to style guide (porting from Zope3 version) 2014-04-19 09:43:11 +02:00
hplattner
5d6abda1df add condition for reload report button 2014-04-14 14:17:10 +02:00
hplattner
f2d50c0de2 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2014-03-31 13:26:23 +02:00
hplattner
888fb8d62c add css class to report meta 2014-03-31 13:26:12 +02:00
70b473a48a allow specification of filename on download 2014-03-31 11:17:26 +02:00
ad30929cfb allow creation of resources within special concepts via showCreateResource option 2014-03-31 10:48:37 +02:00
40cc2c4079 fix removal of versions; apply also to versionable concepts 2014-03-05 09:31:37 +01:00
930249e057 use onclick event for printing instead of href to avoid side effects 2014-02-27 15:55:06 +01:00
2d1830b9b8 gracefully handle integer UIDs in tracks 2014-02-17 08:53:25 +01:00
598b2cc7b8 allow executing of reports on loops root object (no type options) 2014-02-16 09:43:28 +01:00
7c6145ca03 add dbtype for subreport field 2014-02-14 09:36:17 +01:00
58bab52f51 add field properties for controlling export of query results to an external database 2014-02-09 10:26:04 +01:00
9079d8e23e fix type checking: avoid error for objects not providing a type 2014-01-22 16:59:24 +01:00
hplattner
94165f41bb add structure tag to multiline report fields 2014-01-21 15:57:19 +01:00
f7224d80f8 ignore searches for certaing types if no or to short search text is given, using option loops.expert.search.minlen_text 2014-01-12 17:15:00 +01:00
cd1f36c694 dirty hack: suppress searching for accounts when no search text is given 2014-01-09 11:33:52 +01:00
d93074a038 limit tracking of changis to types specified in global option (if given) 2014-01-05 14:45:56 +01:00
311a3e5db4 tolerate ill-formed values with only one item 2014-01-02 17:28:28 +01:00
efcab23914 ignore search for concepts without specification of type or title 2013-12-15 19:35:49 +01:00
0263eacb3b Merge branch 'master' into bbmaster 2013-12-14 09:28:09 +01:00
48b1bf72ec record states definition in change record for transition 2013-11-28 09:32:12 +01:00
c14aee9a9a provide an additional field that shows date and time in one field 2013-11-27 15:53:20 +01:00
b926f6601b provide utility function to get groups the current (or a given) user belongs to: remove prefix if present 2013-11-24 12:13:15 +01:00
b1d70fca1d provide utility function to get groups the current (or a given) user belongs to 2013-11-24 10:54:36 +01:00
3d4b823c8d Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2013-11-22 10:59:57 +01:00
hplattner
7e565c20b3 don't use limits if already calculated 2013-11-22 10:54:32 +01:00
3a332b368c merge branch master 2013-11-16 13:19:01 +01:00
hplattner
693ffb7b63 enable own change state forms 2013-11-07 16:23:41 +01:00
hplattner
037df7cb61 hide status widget if there is no states for object 2013-09-12 12:38:53 +02:00
8a4f612c77 handle relation attributes in child relation set 2013-09-12 12:05:11 +02:00
068c555903 Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2013-08-20 08:41:10 +02:00
hplattner
29f4d1c4fd redirect to url after change state action 2013-08-19 17:12:04 +02:00
hplattner
87046a0276 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-08-08 16:32:30 +02:00
hplattner
e9e58a4e7a hide transitions markup if no transitions available 2013-08-08 16:32:12 +02:00
077e74ca72 use interaction for identifying principal if request is not given 2013-08-01 08:21:14 +02:00
44b20b9da9 suppress change tracking on import 2013-07-31 08:54:17 +02:00
7f95a66c99 merge branch master 2013-07-26 09:42:05 +02:00
hplattner
23f922cbdb Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-07-18 16:38:03 +02:00
e4fb003448 fix object to be updatedt: 'target' instead of 'context' 2013-07-18 16:32:32 +02:00
hplattner
c14846045f Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-07-18 15:01:51 +02:00
f48bfc692e add convenience property to stateful adapter 2013-07-18 14:19:58 +02:00
hplattner
fe2683ca9b Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-07-17 16:19:29 +02:00
7e70140f52 merge branch master 2013-07-16 18:13:05 +02:00
0826275a34 Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2013-07-16 18:11:48 +02:00
hplattner
8510b2997d Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-07-16 15:42:30 +02:00
a9d865a1d0 use state title for display not the internal state name 2013-07-03 19:31:24 +02:00
136f8f76e0 Merge branch 'master' into bbmaster 2013-07-03 15:34:27 +02:00
fc883f4446 Merge branch 'master' into bbmaster 2013-07-03 12:45:39 +02:00
hplattner
639229694f Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-06-27 10:26:20 +02:00
6421c50611 always return a list of address lines, never None 2013-06-27 10:26:04 +02:00
hplattner
531252cc88 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2013-06-24 11:16:14 +02:00
hplattner
5c37097dfa add gitignore values 2013-06-24 11:15:50 +02:00
df964a6db8 force break before text item 2013-06-24 10:48:49 +02:00
8c72be4c39 fix formatting of state info 2013-06-24 10:47:56 +02:00
34a78e073e fix presentation of state in portlet 2013-06-23 12:19:26 +02:00
f2fbcb52c3 merge branch master 2013-06-23 09:46:14 +02:00
12ee37881d allow suppressing of security propagation via noPropagateSecurity option 2013-05-22 15:08:24 +02:00
ce22ab2a4b provide create_object action for allow creation of resources within specific concept types; do not limit book views to Lobo skin 2013-05-21 11:36:19 +02:00
3c43a14af9 merge branch master 2013-04-27 15:15:35 +02:00
aed1c385c5 allow setting of var directory via application startup code 2013-04-18 16:58:33 +02:00
0870b8026d merge branch master 2013-04-01 12:43:26 +02:00
a67c19659a more flexible layout for states info and stateful actions; improved/more flexible translateion facilities 2013-03-24 17:15:06 +01:00
b5ab23a90b merge branch master 2013-03-23 11:32:29 +01:00
fb13f1b00a minor doc fixes 2013-02-06 10:22:20 +01:00
badbd15f2b more info on error when creating or updating person 2012-11-11 11:18:55 +01:00
7ed73e28b5 merge branch master 2012-11-09 09:48:26 +01:00
d50498bdaf merge branch master 2012-10-15 08:20:14 +02:00
8acf5d8a65 handle form values correctly when form is re-displayed because of an error 2012-09-25 16:59:01 +02:00
32171a4e2d show empty fields with flag 'showEmpty' 2012-09-25 16:58:05 +02:00
583a75da9d improve reporting's state field 2012-09-05 17:03:27 +02:00
17012129b8 merge branch master 2012-09-05 16:17:15 +02:00
7a48f12735 add view attribute to job manager 2012-08-13 12:44:59 +02:00
c9ee6d1aac fix performance problem in relation registry 2012-07-31 14:44:39 +02:00
e0ea52cb1d fix display style setting 2012-07-30 15:06:59 +02:00
c38af3274c delay search on relation field (like for other filtering select fields) 2012-07-23 15:10:59 +02:00
aa9661a681 provide additional control on what appears in the label of a datatable item 2012-07-23 13:40:31 +02:00
27720799f8 merge branch master 2012-07-21 08:20:06 +02:00
f4e6b6e3f2 report parameter entry, execution: translations, layout improvement 2012-07-21 08:00:53 +02:00
59d5174f21 show download button on report view if appropriate 2012-07-20 14:31:33 +02:00
e21adfa364 Merge branch 'master' into bbmaster 2012-07-16 10:05:38 +02:00
edf00a3597 keep result set in report or result view for later access 2012-07-10 16:47:02 +02:00
hplattner
7761d74108 fix: use self.type for ReportInstance name 2012-07-10 11:14:20 +02:00
e6e9d4de82 provide storage for result sets in report views 2012-07-10 10:40:47 +02:00
e3ac0a35e9 give report and report instance a name 2012-07-10 10:24:50 +02:00
0aa539e8f8 merge branch master 2012-07-10 09:08:25 +02:00
3fdc16f5c5 Merge branch 'master' into bbmaster 2012-07-08 18:54:05 +02:00
394192474c rename 'float' field to 'decimal' 2012-07-07 15:42:16 +02:00
0087eefc08 provide separate float field instance; CSS improvement (view modes/tabs) 2012-07-07 10:05:57 +02:00
9cef218aaa provide 'Loopy' body macro via view 2012-07-04 09:04:21 +02:00
54c3a9e8aa merge branch master 2012-07-03 13:15:54 +02:00
hplattner
b70a9e00a7 add: custom template for SubReportField 2012-06-28 08:58:32 +02:00
35490794fa accept other values as empty (not only None), e.g. for group header fields 2012-06-26 15:31:08 +02:00
8b49d14bc9 fix virtual target handling 2012-06-19 10:12:15 +02:00
6cf01f6431 suppress controller setup for target in create forms 2012-06-18 16:52:32 +02:00
b7f1115536 use type's name prefix and concept manager settings when creating new concepts 2012-06-18 15:11:50 +02:00
2fdf3a99b9 merge branch master - important: 'limits' parameter for reports 2012-06-18 10:29:24 +02:00
f264a9974d remove obsolete line 2012-06-18 10:06:07 +02:00
7d2a66b3c3 revert last change 2012-06-05 18:05:49 +02:00
dc5f579511 tolerate empty (None) userNam in tracks 2012-06-05 17:58:41 +02:00
343faaa603 allow for explicit setting of name when creating object; simple property to get instances of a type 2012-06-03 19:23:23 +02:00
9bc0c44e12 Merge branch 'master' into bbmaster 2012-06-01 14:37:52 +02:00
2ecf05e5e9 Merge branch 'master' into bbmaster 2012-06-01 08:28:11 +02:00
261ca5b830 Merge branch 'master' into bbmaster 2012-05-24 07:59:53 +02:00
7813f8d806 merge branch master 2012-05-20 15:58:52 +02:00
f9dca48cde merge branch master 2012-05-15 16:52:55 +02:00
a3a90f12fe improve file layout (separation of macros) 2012-05-15 16:44:09 +02:00
8392c763a0 centered alignment for date field 2012-05-01 14:20:17 +02:00
2283accd32 more standard fields; set css class where appropriate, + class definitions in style sheets 2012-05-01 10:42:53 +02:00
381e26edaa new integer and integer URL report fields 2012-04-30 22:56:27 +02:00
b605561acd provide convenience property 'loopsRoot' for concept adapters 2012-04-30 10:49:06 +02:00
8adb6131e1 Merge branch 'master' into bbmaster 2012-04-28 08:51:52 +02:00
f24851fa8b merge branch master 2012-04-25 13:59:06 +02:00
eaa762e081 make sure base object is used as target 2012-04-23 10:17:18 +02:00
ba108d4625 Merge branch 'master' into bbmaster 2012-04-14 12:39:47 +02:00
5dd5a800e5 make doctest system-independent 2012-04-13 16:06:26 +02:00
hplattner
5c908b7b17 add computed totals 2012-04-13 09:51:35 +02:00
hplattner
0a1ba00684 add: default styles 2012-04-12 17:27:49 +02:00
hplattner
a41a541baf add default style for DecimalField 2012-04-12 11:21:15 +02:00
d583f0e980 make multiline field tolerate scalar values; header row factory is now special class assigned in base class 2012-04-11 12:35:49 +02:00
7b1f04e7ab Merge branch 'master' into bbmaster 2012-04-07 12:27:05 +02:00
8db26cce5c merge development stuff from hplattner 2012-04-07 12:26:25 +02:00
hplattner
86e25111b5 fix: replace Combined/CategoryResultSets with computed HeaderRows 2012-04-03 15:20:59 +02:00
b4a888c650 improve duplicates filtering routing (and fix spelling) 2012-03-30 10:43:16 +02:00
hplattner
6b99765ccf add: CombinedResultSet 2012-03-30 08:35:57 +02:00
hplattner
459bd2d6bf add: support for category rows/ execution steps 2012-03-29 16:03:36 +02:00
hplattner
43e51e7538 make colspan/ colwidth/ coltextalign configurable 2012-03-27 15:00:43 +02:00
hplattner
ec319c1782 add: multivalue field 2012-03-22 17:18:10 +01:00
ce5924333c Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2012-03-19 19:53:24 +01:00
hplattner
50c932e3c0 fix: report results template without td 2012-03-19 19:48:44 +01:00
611ad219fa provide appropriate default definition for sort value; extend identifier query to support '*' queries 2012-03-19 19:06:15 +01:00
288b9f62b2 Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2012-03-19 18:52:09 +01:00
hplattner
aa8bc152ba Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-19 10:13:27 +01:00
hplattner
021150af47 fix: template for multiline field 2012-03-19 10:13:10 +01:00
5a6111acb7 vocabulary: handle integer and string tokens as equivalent 2012-03-17 16:53:42 +01:00
90466aad79 Merge branch 'master' into bbmaster 2012-03-17 11:56:38 +01:00
f675c68997 Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2012-03-17 11:55:55 +01:00
ec1628dd00 fix generation of result set for subreport fields 2012-03-17 11:53:39 +01:00
hplattner
4ea797c19f add: .project to gitignore gile 2012-03-16 15:42:11 +01:00
hplattner
9620ed0c95 add: multiline report field 2012-03-15 17:42:01 +01:00
hplattner
65ce2d9779 Merge branch 'master' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-15 12:41:25 +01:00
01a5396bd5 use field (col) instead of renderer name for getColumnRenderer() call 2012-03-14 18:43:54 +01:00
hplattner
008d932184 fix: subReport macro 2012-03-14 17:34:19 +01:00
hplattner
e902c898f4 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-14 16:56:28 +01:00
942de85d87 check for principal is None - may happen when zope.authentication is patched 2012-03-12 08:14:44 +01:00
d496dc001a Merge branch 'master' into bbmaster 2012-03-11 17:19:28 +01:00
b1cb9456fa improve decimal field: use pattern, show right-aligned 2012-03-08 16:55:52 +01:00
hplattner
f682db7811 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-08 16:33:24 +01:00
6e7219bdce provide decimal field with locale-dependent formatting 2012-03-08 15:30:01 +01:00
5eda7ef9c7 show name of track if title is empty 2012-03-08 15:29:40 +01:00
hplattner
b536f8540d Merge branch 'master' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-07 16:14:32 +01:00
d8c7ae86af ignore missing concept type error 2012-03-05 12:40:39 +01:00
1f681de414 ignore errors because of missing principal; allow overwriting password when principal exists 2012-03-05 12:25:02 +01:00
f03f11372e Merge branch 'master' into bbmaster 2012-03-05 10:00:51 +01:00
25525133a2 Merge branch 'master' into bbmaster 2012-03-01 17:30:28 +01:00
931f308fc1 Merge branches 'bbmaster' and 'master' of ssh://git.cy55.de/home/git/loops into bbmaster 2012-03-01 17:24:21 +01:00
6e36c6708c Merge branch 'master' into bbmaster 2012-02-26 10:24:37 +01:00
hplattner
073c9eb7fc add: timestamp support for DateField class 2012-02-09 17:42:58 +01:00
4db06cd046 add date field with locale-dependent output formatting 2012-02-08 11:49:46 +01:00
d3ac5bc8bc allow configuration of display value for relation fields 2012-02-07 15:06:24 +01:00
13507c06a6 Merge branch 'master' into bbmaster 2012-02-07 09:34:57 +01:00
dbf653b900 Merge branch 'master' into bbmaster 2012-01-29 17:23:05 +01:00
36c70374b3 merge branch master into bbmaster 2012-01-29 11:27:43 +01:00
f193e00dcb node traverser: store only loops-specific view as target view in request annotations 2012-01-25 11:27:12 +01:00
ea1fbc9136 use target view for adapted object determined by node traverser if available 2012-01-24 19:38:51 +01:00
3132fc9947 Merge branch 'master' into bbmaster 2012-01-23 11:11:31 +01:00
2426e580ff new report field RelationField 2012-01-20 13:30:16 +01:00
335c52e3cb Merge branch 'master' into bbmaster 2012-01-19 18:18:56 +01:00
2c141a9285 Merge branch 'master' into bbmaster 2012-01-05 12:37:20 +01:00
cebc045348 Merge branch 'master' into bbmaster 2011-12-28 08:36:54 +01:00
ca8267306a Merge branch 'master' into bbmaster 2011-12-23 14:23:51 +01:00
99d027efe4 use keytable field for datatable data 2011-12-23 14:22:32 +01:00
15f622f373 merge branch master 2011-12-11 14:21:39 +01:00
a10b55117b merge branch master 2011-12-09 09:16:36 +01:00
767d5569eb Merge branch 'master' into bbmaster 2011-12-04 11:43:46 +01:00
d6c1ec466c minor clean-up: remove obsolete comments and duplicate code 2011-12-04 11:41:53 +01:00
0d4019de74 Merge branch 'master' into bbmaster 2011-12-04 11:31:50 +01:00
a218d22b93 Merge branch 'master' into bbmaster 2011-12-04 11:24:43 +01:00
c4bcc55bae provide common definitions for tracking storage (tracks, records) adapters 2011-12-04 11:23:51 +01:00
991e78a1fe fix line gone lost during merge of branch master 2011-12-02 10:40:59 +01:00
5f95ef439e merge branch master 2011-12-02 10:16:56 +01:00
837bf66e5c suppress security propagation for performance reasons 2011-11-21 14:59:18 +01:00
bd13c779b6 tolerate missing title when sorting 2011-11-21 14:52:46 +01:00
02e07c0e85 performance improvement: only create new relation if necessary, simplified 2011-11-21 12:35:07 +01:00
c6638e7b5e performance improvement: only create new relation if necessary, simplified 2011-11-21 12:30:00 +01:00
1d5b525a59 performance improvement: only create new relation if necessary 2011-11-21 12:11:31 +01:00
341371604c Merge branch 'master' into bbmaster 2011-11-19 10:13:39 +01:00
7bc3671d74 work in progress: breadcrumbs 2011-11-19 10:12:53 +01:00
274c5612b9 fix bug in ExternalSourceInfo implementation 2011-11-17 11:37:23 +01:00
073d483bf0 Merge branch 'master' into bbmaster 2011-11-17 11:22:34 +01:00
4fba2c1e27 provide field instance with context and request information, e.g. for use in persistent vocabularies 2011-11-14 15:10:11 +01:00
907e9c4da8 provide display rendering for relation property with link (like for relation set) 2011-11-14 13:50:20 +01:00
153f0ee0e7 merge branch master 2011-11-11 15:27:12 +01:00
4a5a596633 better control of filtering (ignore filters and hiding of children in management views 2011-11-11 15:23:36 +01:00
3b6a5d0830 Merge branch 'master' into bbmaster 2011-11-08 13:38:55 +01:00
2b29428e32 more fault-tolerance: avoid errors when title or description is None 2011-11-08 13:37:36 +01:00
1738590455 use BlueBream-compatible security declaration 2011-11-02 10:46:35 +01:00
fd2d9794a5 merge branch master 2011-10-28 17:48:20 +02:00
d25680b94e provide basic functionality for rendering of complex relations 2011-10-28 17:42:24 +02:00
78abe5f55d provide basic functionality for rendering of complex relations.
- provide controlling of rendering macro via field instance method getRenderer()
- add getRelations() method to child relation set
2011-10-28 13:08:37 +02:00
268a1e47ed quick fix for import of predicates with interface definitions 2011-10-28 13:01:30 +02:00
0c601f373b fix check for predicate option 2011-10-24 10:38:54 +02:00
26ef8449b5 use 'hide_children' option for hiding children in listings 2011-10-24 09:23:38 +02:00
5e98c1cb6a Merge branch 'bbmaster' of ssh://git.cy55.de/home/hplattner/git/loops into bbmaster 2011-10-20 21:08:44 +02:00
hplattner
40a5134648 fix: split MemberRegistration to enable only Pricipal creationi 2011-10-20 17:56:27 +02:00
9f5133152f tolerate None values in title and description 2011-10-20 17:48:59 +02:00
hplattner
f8abff58d6 Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster 2011-10-20 14:35:00 +02:00
6d5f273f28 Merge branch 'master' of ssh://git.cy55.de/home/helmutm/git/loops into bbmaster 2011-10-04 17:01:30 +02:00
0186b33146 add zope.app.session to requirements: allow configuration of session storage 2011-10-04 17:00:38 +02:00
2f74a6dc81 make loops package work with BlueBream 1.0: system running 2011-09-29 22:27:06 +02:00
8dced7ecfa work in progress: make loops package work with BlueBream 1.0 2011-09-29 19:22:51 +02:00
8dc0d49972 work in progress: make loops package work with BlueBream 1.0 2011-09-29 19:21:12 +02:00
561 changed files with 6160 additions and 5502 deletions

14
.gitignore vendored
View file

@ -1,4 +1,14 @@
*.pyc
.project
.pydevproject
*.pyo
*.swp
dist/
var/
*.egg-info
*.project
*.pydevproject
*.ropeproject
*.sublime-project
*.sublime-workspace
.env
.settings
adminuser.zcml

View file

@ -6,8 +6,13 @@ $Id$
1.1
---
<<<<<<< HEAD
- provide controlling of rendering macro via field instance method getRenderer()
- add getRelations() method to child relation set
=======
- use targetView entry in request annotations for calling views declared
for adapted concept map objects (i.e. type interfaces)
>>>>>>> master
- Lobo layout: provide new part: image grid; make sure image is not repeated if
it already appears in header part
- new special view 'listsubobjects' for nodes

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (C) 2023 cyberconcepts.org team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,222 +0,0 @@
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

11
MANIFEST.in Normal file
View file

@ -0,0 +1,11 @@
global-include *.cfg
global-include *.css *.js
global-include *.gif *.jpg *.png
global-include *.dmp
global-include *.md *.txt
global-include *.mo *.po *.pot
global-include *.pdf
global-include *.pt
global-include *.zcml
graft loops/integrator/testdata

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# Introduction
This is the main part of the code of the semantic
web application platform *loops*, based on
Zope 3 / bluebream.
More information: see https://www.cyberconcepts.org.

View file

@ -1,22 +0,0 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
$Id$
"""

View file

@ -1,61 +0,0 @@
#
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
$Id$
"""
from zope.app.security.interfaces import IAuthentication
from zope.app.security.interfaces import ILogout, IUnauthenticatedPrincipal
from zope import component
from zope.interface import implements
from loops.browser.node import NodeView
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
class LoginForm(NodeView):
template = ViewPageTemplateFile('auth.pt')
@Lazy
def macro(self):
return self.template.macros['login_form']
@Lazy
def item(self):
return self
class Logout(object):
implements(ILogout)
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
nextUrl = self.request.get('nextURL') or self.request.URL[-1]
if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
auth = component.getUtility(IAuthentication)
ILogout(auth).logout(self.request)
return self.request.response.redirect(nextUrl)

19
config.py Normal file
View file

@ -0,0 +1,19 @@
# loops/config.py
# (used for testing only)
from dotenv import load_dotenv
from os import getenv
load_dotenv()
server_port = getenv('SERVER_PORT', '8099')
app_factory = zope_app_factory
# storage settings
dbengine = 'postgresql+psycopg'
dbname = getenv('DBNAME', 'demo')
dbuser = getenv('DBUSER', 'demo')
dbpassword = getenv('DBPASSWORD', 'secret')
dbschema = getenv('DBSCHEMA', 'demo')

View file

@ -1,104 +0,0 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
View classes for export of report results.
"""
import csv
from cStringIO import StringIO
from zope.cachedescriptors.property import Lazy
from zope.i18n import translate
from loops.common import normalizeName
from loops.expert.browser.report import ResultsConceptView
from loops.interfaces import ILoopsObject
from loops.util import _
class ResultsConceptCSVExport(ResultsConceptView):
isToplevel = True
reportMode = 'export'
delimiter = ';'
#encoding = 'UTF-8'
#encoding = 'ISO8859-15'
#encoding = 'CP852'
@Lazy
def encoding(self):
enc = self.globalOptions('csv_encoding')
if enc:
return enc[0]
return 'UTF-8'
def getFileName(self):
return normalizeName(self.context.title)
def getColumnTitle(self, field):
lang = self.languageInfo.language
return translate(_(field.title), target_language=lang)
def __call__(self):
fields = self.displayedColumns
fieldNames = [f.name for f in fields]
output = StringIO()
writer = csv.DictWriter(output, fieldNames, delimiter=self.delimiter)
output.write(self.delimiter.join(
[self.getColumnTitle(f) for f in fields]) + '\n')
results = self.reportInstance.getResults()
for row in results:
data = {}
for f in fields:
value = f.getValue(row)
if ILoopsObject.providedBy(value):
value = value.title
value = encode(value, self.encoding)
data[f.name] = value
writer.writerow(data)
text = output.getvalue()
self.setDownloadHeader(text)
return text
def setDownloadHeader(self, text):
response = self.request.response
response.setHeader('Content-Disposition',
'attachment; filename=%s.csv' %
self.getFileName())
response.setHeader('Cache-Control', '')
response.setHeader('Pragma', '')
response.setHeader('Content-Type', 'text/csv')
response.setHeader('Content-Length', len(text))
def encode(text, encoding):
if not isinstance(text, unicode):
return text
try:
return text.encode(encoding)
except UnicodeEncodeError:
result = []
for c in text:
try:
result.append(c.encode(encoding))
except UnicodeEncodeError:
result.append('?')
return ''.join(result)
return '???'

View file

@ -1,81 +0,0 @@
<html i18n:domain="loops">
<div metal:define-macro="main">
<div tal:define="report item/reportInstance;
reportView nocall:item;
renderer item/resultsRenderer"
tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post" name="report_data">
<tal:hidden define="params item/dynamicParams"
tal:condition="nothing">
<input type="hidden"
tal:repeat="name params"
tal:attributes="name name;
value params/?name" /></tal:hidden>
<div metal:use-macro="item/report_macros/params" />
<div metal:define-macro="buttons">
<input type="submit" name="report_execute" value="Execute Report"
tal:condition="item/queryFields"
i18n:attributes="value" />
</div>
</form>
<tal:list condition="renderer">
<div metal:use-macro="renderer" />
</tal:list>
<tal:list condition="not:renderer">
<div metal:use-macro="view/concept_macros/conceptchildren" />
</tal:list>
</div>
</div>
<metal:block define-macro="params">
<metal:block use-macro="item/report_macros/query" />
</metal:block>
<metal:block define-macro="query"
tal:define="criteria
item/reportInstance/queryCriteria/parts|nothing">
<table>
<tr tal:repeat="field item/queryFields">
<tal:field define="fieldType field/fieldType;
crit python:criteria and criteria.get(field.name)">
<td><b><span tal:content="field/title" />: </b></td>
<td><metal:field use-macro="item/report_macros/?fieldType" /></td>
</tal:field>
</tr>
</table>
<br />
</metal:block>
<metal:field define-macro="textline">
<input tal:attributes="name string:${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="number">
<input tal:attributes="name string:query.field.${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="date">
<input dojoType="dijit.form.DateTextBox" style="width: 8em"
constraints="{datePattern: 'd.M.y',
min: '1980-01-01', max: '2020-12-31'}"
tal:attributes="name string:${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="selection">
<metal:use use-macro="item/report_macros/textline" />
</metal:field>
</html>

View file

@ -1,44 +0,0 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Filter query results.
$Id$
"""
from zope.interface import implements
from loops.expert.interfaces import IQueryInstance
class QueryInstance(object):
implements(IQueryInstance)
def __init__(self, query, *filters, **kw):
self.query = query
self.filters = filters
self.filterQueries = {}
for k, v in kw.items():
setattr(self, k, v)
def apply(self, uidsOnly=False):
result = self.query.apply()
return result

View file

@ -0,0 +1,14 @@
<configure xmlns="http://namespaces.zope.org/zope">
<principal
id="zope.manager"
title="Manager"
login="admin"
password="admin"
password_manager="Plain Text" />
<grant
role="zope.Manager"
principal="zope.manager" />
</configure>

View file

@ -0,0 +1,11 @@
<configure xmlns="http://namespaces.zope.org/zope">
<include package="loops" file="bluebream.zcml" />
<include package="loops" file="securitypolicy.zcml" />
<include file="adminuser.zcml" />
<includeOverrides file="overrides.zcml" />
</configure>

9
inst/bluebream/config.py Normal file
View file

@ -0,0 +1,9 @@
# loops/inst/bluebream/config.py
from dotenv import load_dotenv
from os import getenv
load_dotenv()
server_port = getenv('SERVER_PORT', '8099')

8
inst/bluebream/env.in Normal file
View file

@ -0,0 +1,8 @@
# loops/inst/bluebream/.env
SERVER_PORT=8800
DBNAME=ccotest
DBUSER=ccotest
DBPASSWORD=cco
DBSCHEMA=testing

15
inst/bluebream/main.py Normal file
View file

@ -0,0 +1,15 @@
# loops/inst/bluebream/main.py
import waitress
from zope.app.wsgi import config, getWSGIApplication
def run(app, config):
port = int(config.server_port)
#print(f'Serving on port {port}.')
waitress.serve(app, port=port)
if __name__ == '__main__':
import config
app = getWSGIApplication('zope.conf')
run(app, config)

View file

@ -0,0 +1,4 @@
<configure xmlns="http://namespaces.zope.org/zope">
</configure>

39
inst/bluebream/zope.conf Normal file
View file

@ -0,0 +1,39 @@
# loops/inst/bluebream/zope.conf
# main zope configuration file for deployment
# Identify the component configuration used to define the site:
site-definition application.zcml
<zodb>
<filestorage>
path var/filestorage/Data.fs
blob-dir var/blob
</filestorage>
# Uncomment this if you want to connect to a ZEO server instead:
# <zeoclient>
# server localhost:8100
# storage 1
# # ZEO client cache, in bytes
# cache-size 20MB
# # Uncomment to have a persistent disk cache
# #client zeo1
# </zeoclient>
</zodb>
<eventlog>
# This sets up logging to both a file and to standard output (STDOUT).
# The "path" setting can be a relative or absolute filesystem path or
# the tokens STDOUT or STDERR.
<logfile>
path var/log/bluebream.log
formatter zope.exceptions.log.Formatter
</logfile>
<logfile>
path STDOUT
formatter zope.exceptions.log.Formatter
</logfile>
</eventlog>

View file

@ -0,0 +1,14 @@
<configure xmlns="http://namespaces.zope.org/zope">
<principal
id="zope.manager"
title="Manager"
login="admin"
password="admin"
password_manager="Plain Text" />
<grant
role="zope.Manager"
principal="zope.manager" />
</configure>

View file

@ -0,0 +1,33 @@
<configure xmlns="http://namespaces.zope.org/zope">
<include package="loops" file="bluebream.zcml" />
<include package="loops" file="securitypolicy.zcml" />
<module module="loops.patch" />
<include file="adminuser.zcml" />
<include package="cybertools" />
<include package="cybertools.ajax.dojo" />
<include package="cybertools.catalog" />
<include package="cybertools.composer.layout" />
<include package="cybertools.container" />
<!--<include package="cybertools.pyscript" />-->
<!--<include package="cybertools.xedit" />-->
<include package="loops" />
<!--<include package="cco.schema" />
<include package="cco.skin.r2" />
<include package="cco.webapi" />
<include package="cyberapps.ccmkg" />
<include package="cyberapps.knowledge" />-->
<include package="loops.server" file="auth.zcml" />
<!-- Override registrations -->
<includeOverrides package="loops" file="overrides.zcml" />
<includeOverrides file="overrides.zcml" />
</configure>

41
inst/loops/config.py Normal file
View file

@ -0,0 +1,41 @@
# loops/inst/loops/config.py
from dotenv import load_dotenv
from os import getenv
load_dotenv()
server_id = getenv('SERVER_ID')
zope_conf = getenv('ZOPE_CONF', 'zope.conf')
server_port = getenv('SERVER_PORT',
server_id and getenv(f'SERVER_PORT_{server_id}')) or '8080'
base_url = getenv('BASE_URL',
server_id and getenv(f'BASE_URL_{server_id}')) or 'https://test.example.com'
shell_pw = (getenv('SHELL_PW', 'dummy'))
loops_path = (getenv('LOOPS_PATH', 'loops/demo'))
# storage settings
from scopes.storage.db.postgres import StorageFactory
dbengine = getenv('DBENGINE', 'postgresql+psycopg')
dbname = getenv('DBNAME', 'demo')
dbuser = getenv('DBUSER', 'demo')
dbpassword = getenv('DBPASSWORD', 'secret')
dbschema = getenv('DBSCHEMA', 'demo')
# OpenID Connect (OIDC, e.g. via zitadel) authentication settings
oidc_provider = getenv('OIDC_PROVIDER', '') #'https://instance1-abcdef.zitadel.cloud')
oidc_client_id = getenv('OIDC_CLIENT_ID', '12345')
oidc_params = dict(
op_config_url=oidc_provider + '/.well-known/openid-configuration',
op_uris=None,
op_keys=None,
callback_url=getenv('OIDC_CALLBACK_URL', base_url + '/auth_callback'),
client_id=oidc_client_id,
principal_prefix=getenv('OIDC_PRINCIPAL_PREFIX', 'loops.'),
cookie_name=getenv('OIDC_COOKIE_NAME', 'oidc_' + oidc_client_id),
cookie_domain=getenv('OIDC_COOKIE_DOMAIN', None),
cookie_lifetime=getenv('OIDC_COOKIE_LIFETIME', '86400'),
cookie_crypt=getenv('OIDC_COOKIE_CRYPT', None)
)

12
inst/loops/env.in Normal file
View file

@ -0,0 +1,12 @@
# loops/inst/loops/.env
SERVER_ID=0
SERVER_PORT_0=8800
SHELL_PW=dummy
LOOPS_PATH=demo
DBNAME=loops
DBUSER=demouser
DBPASSWORD=dummy
DBSCHEMA=demo

View file

@ -0,0 +1,4 @@
<configure xmlns="http://namespaces.zope.org/zope">
</configure>

7
inst/loops/runserver.sh Executable file
View file

@ -0,0 +1,7 @@
# inst/loops/runserver.sh
set -a
# use environment variables for instance-specific configuration:
#SERVER_ID=0
python -c "from loops.server.main import main; main()"

39
inst/loops/zope.conf Normal file
View file

@ -0,0 +1,39 @@
# loops/inst/bluebream/zope.conf
# main zope configuration file for deployment
# Identify the component configuration used to define the site:
site-definition application.zcml
<zodb>
<filestorage>
path var/filestorage/Data.$(SERVER_ID).fs
blob-dir var/blob.$(SERVER_ID)
</filestorage>
# Uncomment this if you want to connect to a ZEO server instead:
# <zeoclient>
# server $(ZEO_SERVER)
# storage 1
# # ZEO client cache, in bytes
# cache-size $(ZEO_CACHE_SIZE)
# # Uncomment to have a persistent disk cache
# #client zeo1$(SERVER_ID)
# </zeoclient>
</zodb>
<eventlog>
# This sets up logging to both a file and to standard output (STDOUT).
# The "path" setting can be a relative or absolute filesystem path or
# the tokens STDOUT or STDERR.
<logfile>
path var/log/loops-$(SERVER_ID).log
formatter zope.exceptions.log.Formatter
</logfile>
<logfile>
path STDOUT
formatter zope.exceptions.log.Formatter
</logfile>
</eventlog>

9
inst/loops/zshell.sh Executable file
View file

@ -0,0 +1,9 @@
# inst/loops/zshell.sh
set -a
# use environment variables for instance-specific configuration:
#SERVER_ID=0
#LOOPS_PATH=sites/mysite
python -ic "from loops.server import psu; psu.setup()"

View file

@ -1,54 +0,0 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Access to external objects.
$Id$
"""
import os, re
from zope import component
from zope.cachedescriptors.property import Lazy
from zope.component import adapts
from zope.interface import implements
from cybertools.integrator.interfaces import IContainerFactory
from loops.common import AdapterBase, adapted
from loops.integrator.content.interfaces import IExternalAccess
from loops.interfaces import IConcept
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IExternalAccess,)
class ExternalAccess(AdapterBase):
""" A concept adapter for accessing external collection.
"""
implements(IExternalAccess)
adapts(IConcept)
_contextAttributes = list(IExternalAccess) + list(IConcept)
def __call__(self):
factory = component.getUtility(IContainerFactory, self.providerName)
address = os.path.join(self.baseAddress, self.address or '')
return factory(address, __parent__=self.context)

View file

@ -1,45 +0,0 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Adapter for mail resources.
$Id$
"""
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.component import adapts
from zope.interface import implements
from loops.integrator.mail.interfaces import IMailResource
from loops.resource import TextDocumentAdapter
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IMailResource,)
class MailResource(TextDocumentAdapter):
""" A concept adapter for accessing a mail collection.
May delegate access to a named utility.
"""
implements(IMailResource)
_contextAttributes = list(IMailResource)

View file

@ -1 +0,0 @@
'''package loops.knowledge.qualification'''

View file

@ -1,43 +0,0 @@
#
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Controlling qualification activities of persons.
Central part of CCM competence and certification management framework.
"""
from zope.component import adapts
from zope.interface import implementer, implements
from loops.common import AdapterBase
from loops.interfaces import IConcept
from loops.knowledge.qualification.interfaces import ICompetence
from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (ICompetence,)
class Competence(AdapterBase):
implements(ICompetence)
_contextAttributes = list(ICompetence)

Binary file not shown.

View file

@ -23,7 +23,7 @@ with lower-level aspects like type or state management.
Let's also import some common stuff needed later.
>>> from loops.common import adapted
>>> from loops.common import adapted, baseObject
>>> from loops.setup import addAndConfigureObject
@ -48,14 +48,14 @@ top-level loops container and a concept manager:
>>> cc1 = Concept()
>>> concepts['cc1'] = cc1
>>> cc1.title
u''
''
>>> loopsRoot.getLoopsUri(cc1)
'.loops/concepts/cc1'
>>> cc2 = Concept(u'Zope 3')
>>> cc2 = Concept('Zope 3')
>>> concepts['cc2'] = cc2
>>> cc2.title
u'Zope 3'
'Zope 3'
Now we want to relate the second concept to the first one.
@ -73,11 +73,11 @@ ConceptRelation):
We can now ask our concepts for their related child and parent concepts:
>>> [getName(c) for c in cc1.getChildren()]
[u'cc2']
['cc2']
>>> len(cc1.getParents())
0
>>> [getName(p) for p in cc2.getParents()]
[u'cc1']
['cc1']
>>> len(cc2.getChildren())
0
@ -90,24 +90,24 @@ a special predicate 'hasType'.
>>> typeObject = concepts['type']
>>> typeObject.setConceptType(typeObject)
>>> typeObject.getConceptType().title
u'Type'
'Type'
>>> concepts['unknown'] = Concept(u'Unknown Type')
>>> concepts['unknown'] = Concept('Unknown Type')
>>> unknown = concepts['unknown']
>>> unknown.setConceptType(typeObject)
>>> unknown.getConceptType().title
u'Type'
'Type'
>>> cc1.setConceptType(unknown)
>>> cc1.getConceptType().title
u'Unknown Type'
'Unknown Type'
>>> concepts['topic'] = Concept(u'Topic')
>>> concepts['topic'] = Concept('Topic')
>>> topic = concepts['topic']
>>> topic.setConceptType(typeObject)
>>> cc1.setConceptType(topic)
>>> cc1.getConceptType().title
u'Topic'
'Topic'
We get a list of types using the ConceptTypeSourceList.
In order for the type machinery to work we first have to provide a
@ -124,7 +124,7 @@ type manager.
>>> from loops.concept import ConceptTypeSourceList
>>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types)
[u'Customer', u'Domain', u'Predicate', u'Topic', u'Type', u'Unknown Type']
['Customer', 'Domain', 'Predicate', 'Topic', 'Type', 'Unknown Type']
Using a PredicateSourceList we can retrieve a list of the available
predicates.
@ -136,7 +136,7 @@ Note that the 'hasType' predicate is suppressed from this list as the
corresponding relation is only assigned via the conceptType attribute:
>>> sorted(t.title for t in predicates)
[u'subobject']
['subobject']
Concept Views
-------------
@ -146,7 +146,7 @@ Concept Views
>>> children = list(view.children())
>>> [c.title for c in children]
[u'Zope 3']
['Zope 3']
The token attribute provided with the items returned by the children() and
parents() methods identifies identifies not only the item itself but
@ -159,14 +159,14 @@ of URIs to item and the predicate of the relationship:
There is also a concept configuration view that allows updating the
underlying context object:
>>> cc3 = Concept(u'loops for Zope 3')
>>> cc3 = Concept('loops for Zope 3')
>>> concepts['cc3'] = cc3
>>> view = ConceptConfigureView(cc1,
... TestRequest(action='assign', tokens=['.loops/concepts/cc3']))
>>> view.update()
True
>>> sorted(c.title for c in cc1.getChildren())
[u'Zope 3', u'loops for Zope 3']
['Zope 3', 'loops for Zope 3']
>>> input = {'action': 'remove', 'qualifier': 'children',
... 'form.button.submit': 'Remove Chiildren',
@ -175,18 +175,18 @@ underlying context object:
>>> view.update()
True
>>> sorted(c.title for c in cc1.getChildren())
[u'loops for Zope 3']
['loops for Zope 3']
We can also create a new concept and assign it.
>>> params = {'action': 'create', 'create.name': 'cc4',
... 'create.title': u'New concept',
... 'create.title': 'New concept',
... 'create.type': '.loops/concepts/topic'}
>>> view = ConceptConfigureView(cc1, TestRequest(**params))
>>> view.update()
True
>>> sorted(c.title for c in cc1.getChildren())
[u'New concept', u'loops for Zope 3']
['New concept', 'loops for Zope 3']
The concept configuration view provides methods for displaying concept
types and predicates.
@ -198,15 +198,15 @@ types and predicates.
>>> component.provideAdapter(LoopsTerms, (IIterableSource, IBrowserRequest), ITerms)
>>> sorted((t.title, t.token) for t in view.conceptTypes())
[(u'Customer', '.loops/concepts/customer'),
(u'Domain', '.loops/concepts/domain'),
(u'Predicate', '.loops/concepts/predicate'),
(u'Topic', '.loops/concepts/topic'),
(u'Type', '.loops/concepts/type'),
(u'Unknown Type', '.loops/concepts/unknown')]
[('Customer', '.loops/concepts/customer'),
('Domain', '.loops/concepts/domain'),
('Predicate', '.loops/concepts/predicate'),
('Topic', '.loops/concepts/topic'),
('Type', '.loops/concepts/type'),
('Unknown Type', '.loops/concepts/unknown')]
>>> sorted((t.title, t.token) for t in view.predicates())
[(u'subobject', '.loops/concepts/standard')]
[('subobject', '.loops/concepts/standard')]
Index attributes adapter
------------------------
@ -214,10 +214,10 @@ Index attributes adapter
>>> from loops.concept import IndexAttributes
>>> idx = IndexAttributes(cc2)
>>> idx.text()
u'cc2 Zope 3'
'cc2 Zope 3'
>>> idx.title()
u'cc2 Zope 3'
'cc2 Zope 3'
Resources and what they have to do with Concepts
@ -233,27 +233,27 @@ A common type of resource is a document:
>>> from loops.interfaces import IDocument
>>> from loops.resource import Document
>>> doc1 = Document(u'Zope Info')
>>> doc1 = Document('Zope Info')
>>> resources['doc1'] = doc1
>>> doc1.title
u'Zope Info'
'Zope Info'
>>> doc1.data
u''
''
>>> doc1.contentType
u''
''
We can also directly use Resource objects; these behave like files.
In fact, by using resource types we can explicitly assign a resource
the 'file' type, but we will use this feature later:
>>> img = Resource(u'A png Image')
>>> img = Resource('A png Image')
For testing we use some simple files from the tests directory:
>>> from loops import tests
>>> import os
>>> path = os.path.join(*tests.__path__)
>>> img.data = open(os.path.join(path, 'test_icon.png')).read()
>>> img.data = open(os.path.join(path, 'test_icon.png'), 'rb').read()
>>> img.getSize()
381
>>> img.getImageSize()
@ -261,8 +261,8 @@ For testing we use some simple files from the tests directory:
>>> img.contentType
'image/png'
>>> pdf = Resource(u'A pdf File')
>>> pdf.data = open(os.path.join(path, 'test.pdf')).read()
>>> pdf = Resource('A pdf File')
>>> pdf.data = open(os.path.join(path, 'test.pdf'), 'rb').read()
>>> pdf.getSize()
25862
>>> pdf.getImageSize()
@ -287,7 +287,7 @@ from concepts to resources:
... 'tokens': ['.loops/resources/doc1:.loops/concepts/standard']}
>>> view = ConceptConfigureView(cc1, TestRequest(form=form))
>>> [getName(r.context) for r in view.resources()]
[u'doc1']
['doc1']
>>> view.update()
True
>>> len(cc1.getResources())
@ -316,10 +316,10 @@ Index attributes adapter
>>> component.provideAdapter(FileAdapter, provides=IFile)
>>> idx = IndexAttributes(doc1)
>>> idx.text()
u''
''
>>> idx.title()
u'doc1 Zope Info'
'doc1 Zope Info'
Views/Nodes: Menus, Menu Items, Listings, Pages, etc
@ -343,14 +343,14 @@ The view manager has already been created during setup.
The view space is typically built up with nodes; a node may be a top-level
menu that may contain other nodes as menu or content items:
>>> m1 = views['m1'] = Node(u'Menu')
>>> m11 = m1['m11'] = Node(u'Zope')
>>> m111 = m11['m111'] = Node(u'Zope in General')
>>> m112 = m11['m112'] = Node(u'Zope 3')
>>> m1 = views['m1'] = Node('Menu')
>>> m11 = m1['m11'] = Node('Zope')
>>> m111 = m11['m111'] = Node('Zope in General')
>>> m112 = m11['m112'] = Node('Zope 3')
>>> m112.title
u'Zope 3'
'Zope 3'
>>> m112.description
u''
''
There are a few convienence methods for accessing parent and child nodes:
@ -359,7 +359,7 @@ There are a few convienence methods for accessing parent and child nodes:
>>> m11.getParentNode() is m1
True
>>> [getName(child) for child in m11.getChildNodes()]
[u'm111', u'm112']
['m111', 'm112']
What is returned by these may be controlled by the nodeType attribute:
@ -444,13 +444,13 @@ Node Views
>>> page = view.page
>>> items = page.textItems
>>> for item in items:
... print item.url, item.editable
... print(item.url, item.editable)
http://127.0.0.1/loops/views/m1/m11/m112 False
>>> menu = view.menu
>>> items = menu.menuItems
>>> for item in items:
... print item.url, view.selected(item)
... print(item.url, view.selected(item))
http://127.0.0.1/loops/views/m1/m11 True
A NodeView provides an itemNum attribute that may be used to count elements
@ -493,11 +493,11 @@ view; these views we have to provide as multi-adapters:
>>> len(tt)
9
>>> sorted((t.token, t.title) for t in view.targetTypes())[1]
('.loops/concepts/domain', u'Domain')
('.loops/concepts/domain', 'Domain')
>>> view.update()
True
>>> sorted(resources.keys())
[u'd001.txt', u'd002.txt', u'd003.txt', u'doc1', u'm1.m11.m111']
['d001.txt', 'd002.txt', 'd003.txt', 'doc1', 'm1.m11.m111']
>>> view.target.title, view.target.token
('New Resource', '.loops/resources/m1.m11.m111')
@ -537,28 +537,28 @@ view for rendering.)
>>> component.provideAdapter(LoopsType)
>>> view = NodeView(m112, TestRequest())
>>> view.renderTarget()
u'<pre></pre>'
>>> doc1.data = u'Test data\n\nAnother paragraph'
'<pre></pre>'
>>> doc1.data = 'Test data\n\nAnother paragraph'
>>> view.renderTarget()
u'<pre>Test data\n\nAnother paragraph</pre>'
'<pre>Test data\n\nAnother paragraph</pre>'
>>> doc1.contentType = 'text/restructured'
>>> doc1.data = u'Test data\n\nAnother `paragraph <para>`_'
>>> doc1.data = 'Test data\n\nAnother `paragraph <para>`_'
>>> from loops.wiki.base import wikiLinksActive
>>> wikiLinksActive(loopsRoot)
False
>>> view.renderTarget()
u'<p>Test data</p>\n<p>Another <a class="reference" href="para">paragraph</a></p>\n'
'<p>Test data</p>\n<p>Another <a class="reference external" href="para">paragraph</a></p>\n'
u'<p>Test data</p>\n<p>Another <a class="reference create"
'<p>Test data</p>\n<p>Another <a class="reference create"
href="http://127.0.0.1/loops/wiki/create.html?linkid=0000001">?paragraph</a></p>\n'
>>> #links = loopsRoot.getRecordManager()['links']
>>> #links['0000001']
<Link ['42', 1, '', '... ...', u'para', None]: {}>
<Link ['42', 1, '', '... ...', 'para', None]: {}>
If the target object is removed from its container all references
to it are removed as well. (To make this work we have to handle
@ -661,9 +661,9 @@ Breadcrumbs
>>> view = NodeView(m114, request)
>>> request.annotations.setdefault('loops.view', {})['nodeView'] = view
>>> view.breadcrumbs()
[{'url': 'http://127.0.0.1/loops/views/m1', 'label': u'Menu'},
{'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': u'Zope'},
{'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': u''}]
[{'label': 'Menu', 'url': 'http://127.0.0.1/loops/views/m1'},
{'label': 'Zope', 'url': 'http://127.0.0.1/loops/views/m1/m11'},
{'label': '', 'url': 'http://127.0.0.1/loops/views/m1/m11/m114'}]
End-user Forms and Special Views
@ -705,8 +705,8 @@ been created during setup.
>>> custType = TypeConcept(customer)
>>> custType.options
[]
>>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation')
>>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts')
>>> cust1 = concepts['cust1'] = Concept('Zope Corporation')
>>> cust2 = concepts['cust2'] = Concept('cyberconcepts')
>>> for c in (cust1, cust2): c.conceptType = customer
>>> custType.options = ('qualifier:assign',)
>>> ConceptType(cust1).qualifiers
@ -714,7 +714,7 @@ been created during setup.
>>> form = CreateObjectForm(m112, TestRequest())
>>> form.presetTypesForAssignment
[{'token': 'loops:concept:customer', 'title': u'Customer'}]
[{'title': 'Customer', 'token': 'loops:concept:customer'}]
If the node's target is a type concept we don't get any assignments because
it does not make much sense to assign resources or other concepts as
@ -736,18 +736,18 @@ on data provided in this form:
>>> note_tc = concepts['note']
>>> component.provideAdapter(NameChooser)
>>> request = TestRequest(form={'title': u'Test Note',
... 'form.type': u'.loops/concepts/note',
... 'contentType': u'text/restructured',
... 'linkUrl': u'http://'})
>>> request = TestRequest(form={'title': 'Test Note',
... 'form.type': '.loops/concepts/note',
... 'contentType': 'text/restructured',
... 'linkUrl': 'http://'})
>>> view = NodeView(m112, request)
>>> cont = CreateObject(view, request)
>>> cont.update()
False
>>> sorted(resources.keys())
[...u'test_note'...]
[...'test_note'...]
>>> resources['test_note'].title
u'Test Note'
'Test Note'
If there is a concept selected in the combo box we assign this to the newly
created object:
@ -755,8 +755,8 @@ created object:
>>> from loops import util
>>> topicUid = util.getUidForObject(topic)
>>> predicateUid = util.getUidForObject(concepts.getDefaultPredicate())
>>> request = TestRequest(form={'title': u'Test Note',
... 'form.type': u'.loops/concepts/note',
>>> request = TestRequest(form={'title': 'Test Note',
... 'form.type': '.loops/concepts/note',
... 'form.assignments.selected':
... [':'.join((topicUid, predicateUid))]})
>>> view = NodeView(m112, request)
@ -764,22 +764,22 @@ created object:
>>> cont.update()
False
>>> sorted(resources.keys())
[...u'test_note-2'...]
[...'test_note-2'...]
>>> note = resources['test_note-2']
>>> sorted(t.__name__ for t in note.getConcepts())
[u'note', u'topic']
['note', 'topic']
When creating an object its name may be automatically generated using the title
of the object. Let's make sure that the name chooser also handles special
and possibly critcal cases:
>>> nc = NameChooser(resources)
>>> nc.chooseName(u'', Resource(u'abc: (cde)'))
u'abc__cde'
>>> nc.chooseName(u'', Resource(u'\xdcml\xe4ut'))
u'uemlaeut'
>>> nc.chooseName(u'', Resource(u'A very very loooooong title'))
u'a_title'
>>> nc.chooseName('', Resource('abc: (cde)'))
'abc__cde'
>>> nc.chooseName('', Resource('\xdcml\xe4ut'))
'uemlaeut'
>>> nc.chooseName('', Resource('A very very loooooong title'))
'a_title'
Editing an Object
-----------------
@ -796,7 +796,7 @@ that in turns calls formlibs ``setUpWidgets()``.
The new technique uses the ``fields`` and ``data`` attributes...
>>> for f in view.fields:
... print f.name, f.fieldType, f.required, f.vocabulary
... print(f.name, f.fieldType, f.required, f.vocabulary)
title textline True None
data textarea False None
contentType dropdown True <...SimpleVocabulary object...>
@ -804,22 +804,22 @@ The new technique uses the ``fields`` and ``data`` attributes...
linkText textline False None
>>> view.data
{'linkUrl': u'http://', 'contentType': u'text/restructured', 'data': u'',
'linkText': u'', 'title': u'Test Note'}
{'title': 'Test Note', 'data': '', 'contentType': 'text/restructured',
'linkUrl': 'http://', 'linkText': ''}
The object is changed via a FormController adapter created for
a NodeView.
>>> form = dict(
... title=u'Test Note - changed',
... contentType=u'text/plain',)
... title='Test Note - changed',
... contentType='text/plain',)
>>> request = TestRequest(form=form)
>>> view = NodeView(m112, request)
>>> cont = EditObject(view, request)
>>> cont.update()
False
>>> resources['test_note'].title
u'Test Note - changed'
'Test Note - changed'
Virtual Targets
---------------
@ -883,13 +883,13 @@ informations about all parents of an object.
>>> parents = m113.getAllParents()
>>> for p in parents:
... print p.object.title
... print(p.object.title)
Zope
Menu
>>> parents = resources['test_note'].getAllParents()
>>> for p in parents:
... print p.object.title, len(p.relations)
... print(p.object.title, len(p.relations))
Note 1
Type 2
@ -916,10 +916,30 @@ relates ISO country codes with the full name of the country.
[('at', ['Austria']), ('de', ['Germany'])]
>>> countries.dataAsRecords()
[{'value': 'Austria', 'key': 'at'}, {'value': 'Germany', 'key': 'de'}]
[{'key': 'at', 'value': 'Austria'}, {'key': 'de', 'value': 'Germany'}]
>>> countries.getRowsByValue('value', 'Germany')
[{'value': 'Germany', 'key': 'de'}]
[{'key': 'de', 'value': 'Germany'}]
The ``recordstable`` type is a variation of this datable type that contains
a simple list of records - without a key column. A record in this type is a
dictionary with the field name as key and the field value as value.
>>> from loops.table import IRecordsTable, RecordsTable
>>> component.provideAdapter(RecordsTable, provides=IRecordsTable)
>>> drType = addAndConfigureObject(concepts, Concept, 'recordstable',
... title='Records Table', conceptType=concepts['type'],
... typeInterface=IRecordsTable)
We just reuse the existing ``countries`` table and convert it to a records table.
>>> baseObject(countries).setType(drType)
>>> countries = adapted(concepts['countries'])
>>> countries.data
[{'key': 'at', 'value': 'Austria'}, {'key': 'de', 'value': 'Germany'}]
Caching
@ -931,7 +951,7 @@ To be done...
>>> obj = resources['test_note']
>>> cxObj = cached(obj)
>>> [p.object.title for p in cxObj.getAllParents()]
[u'Note', u'Type']
['Note', 'Type']
Security
@ -940,6 +960,12 @@ Security
>>> from loops.security.browser import admin, audit
Paster Shell Utilities - Repair Scripts
=======================================
>>> from loops.repair.base import removeRecords
Import/Export
=============

17
loops/__init__.py Normal file
View file

@ -0,0 +1,17 @@
# package loops
# intid monkey patch for avoiding ForbiddenAttribute error
from zope import component
from zope.intid.interfaces import IIntIds
from zope import intid
from zope.security.proxy import removeSecurityProxy
def queryId(self, ob, default=None):
try:
return self.getId(removeSecurityProxy(ob))
except KeyError:
return default
intid.IntIds.queryId = queryId

View file

@ -1,33 +1,13 @@
# -*- coding: UTF-8 -*-
# -*- Mode: Python; py-indent-offset: 4 -*-
#
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.base
""" Implementation of loops root object.
"""
$Id$
"""
from zope.app.container.btree import BTreeContainer
from zope.app.folder.folder import Folder
from zope.app.folder.interfaces import IFolder
from zope.container.btree import BTreeContainer
from zope.site.folder import Folder
from zope.site.interfaces import IFolder
from zope.traversing.api import getPath, traverse
from zope.interface import implements
from zope.interface import implementer
from cybertools.util.jeep import Jeep
from loops.interfaces import ILoops
@ -35,17 +15,8 @@ from loops.interfaces import ILoops
loopsPrefix = '.loops'
@implementer(ILoops)
class Loops(Folder):
#class Loops(BTreeContainer):
implements(ILoops)
#def getSiteManager(self):
# return self.__parent__.getSiteManager()
@property
def _SampleContainer__data(self):
return self.data
_skinName = ''
def getSkinName(self): return self._skinName
@ -75,7 +46,8 @@ class Loops(Folder):
return self.get('records')
def getLoopsUri(self, obj):
return str(loopsPrefix + getPath(obj)[len(getPath(self)):])
uri = loopsPrefix + getPath(obj)[len(getPath(self)):]
return uri
def loopsTraverse(self, uri):
prefix = loopsPrefix + '/'

68
loops/bluebream.zcml Normal file
View file

@ -0,0 +1,68 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<include package="zope.component" file="meta.zcml" />
<include package="zope.security" file="meta.zcml" />
<include package="zope.publisher" file="meta.zcml" />
<include package="zope.i18n" file="meta.zcml" />
<include package="zope.browserresource" file="meta.zcml" />
<include package="zope.browsermenu" file="meta.zcml" />
<include package="zope.browserpage" file="meta.zcml" />
<include package="zope.securitypolicy" file="meta.zcml" />
<include package="zope.principalregistry" file="meta.zcml" />
<include package="zope.app.publication" file="meta.zcml" />
<include package="zope.app.form.browser" file="meta.zcml" />
<include package="zope.app.container.browser" file="meta.zcml" />
<include package="zope.browserresource" />
<include package="zope.copypastemove" />
<include package="zope.publisher" />
<include package="zope.component" />
<include package="zope.traversing" />
<include package="zope.location" />
<include package="zope.site" />
<include package="zope.annotation" />
<include package="zope.principalregistry" />
<include package="zope.container" />
<include package="zope.componentvocabulary" />
<include package="zope.formlib" />
<include package="zope.app.appsetup" />
<include package="zope.app.security" />
<include package="zope.app.publication" />
<include package="zope.app.form.browser" />
<include package="zope.app.basicskin" />
<include package="zope.browsermenu" />
<include package="zope.authentication" />
<include package="zope.securitypolicy" />
<include package="zope.login" />
<include package="zope.session" />
<include package="zope.error" />
<include package="zope.app.zcmlfiles" file="menus.zcml" />
<include package="zope.app.authentication" />
<include package="zope.app.security.browser" />
<include package="zope.app.catalog" />
<include package="zope.traversing.browser" />
<include package="zope.browserpage" />
<include package="zope.app.schema" />
<include package="zope.app.http" />
<include package="zope.keyreference" />
<include package="zope.intid" />
<include package="zope.contentprovider" />
<include package="zope.i18n" />
<include package="zope.catalog" />
<include package="zope.dublincore.browser" />
<include package="zope.app.zcmlfiles" />
<include package="zope.app.i18n" />
<include package="zope.app.intid" />
<include package="zope.app.renderer" />
<include package="zope.app.session" />
<include package="zope.sendmail" file="meta.zcml" />
<browser:defaultView
for="zope.container.interfaces.IContainer"
name="index.html" />
</configure>

View file

@ -1,26 +1,9 @@
#
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.action
"""
Base classes (sort of views) for action portlet items.
""" Base classes (sort of views) for action portlet items.
"""
from urllib import urlencode
from urllib.parse import urlencode
from zope import component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
@ -125,6 +108,15 @@ actions.register('external_edit', 'object', TargetAction,
cssClass='icon-action',
)
actions.register('create_object', 'portlet', DialogAction,
title=_('Create Resource...'),
description=_('Create a new resource object.'),
viewName='create_object.html',
dialogName='edit',
prerequisites=['registerDojoEditor'],
permission='zope.ManageContent',
)
actions.register('edit_object', 'portlet', DialogAction,
title=_(u'Edit Resource...'),
description=_(u'Modify resource object.'),

78
loops/browser/auth.py Normal file
View file

@ -0,0 +1,78 @@
# loops.browser.auth
""" Login, logout, unauthorized stuff.
"""
from zope.app.exception.browser.unauthorized import Unauthorized as DefaultUnauth
from zope.authentication.interfaces import IAuthentication
from zope.authentication.interfaces import ILogout, IUnauthenticatedPrincipal
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.interface import implementer
from loops.browser.concept import ConceptView
from loops.browser.node import NodeView
template = ViewPageTemplateFile('auth.pt')
class LoginConcept(ConceptView):
template = template
@Lazy
def macro(self):
return self.template.macros['login_form']
class LoginForm(NodeView):
template = template
@Lazy
def macro(self):
return self.template.macros['login_form']
@Lazy
def item(self):
return self
@implementer(ILogout)
class Logout(object):
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
nextUrl = self.request.get('nextURL') or self.request.URL[-1]
if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
auth = component.getUtility(IAuthentication)
ILogout(auth).logout(self.request)
return self.request.response.redirect(nextUrl)
class Unauthorized(ConceptView):
isTopLevel = True
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
response = self.request.response
response.setStatus(403)
# make sure that squid does not keep the response in the cache
response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
response.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate')
response.setHeader('Pragma', 'no-cache')
if self.nodeView is None:
v = DefaultUnauth(self.context, self.request)
return v()
url = self.nodeView.topMenu.url
response.redirect(url + '/unauthorized')

94
browser/common.py → loops/browser/common.py Normal file → Executable file
View file

@ -1,45 +1,28 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.common
"""
Common base class for loops browser view classes.
""" Common base class for loops browser view classes.
"""
from cgi import parse_qs, parse_qsl
#import mimetypes # use more specific assignments from cybertools.text
from datetime import date, datetime
from logging import getLogger
import re
from time import strptime
from urllib import urlencode
from urllib.parse import parse_qs, parse_qsl, urlencode
from zope import component
from zope.app.form.browser.interfaces import ITerms
from zope.app.i18n.interfaces import ITranslationDomain
from zope.app.security.interfaces import IAuthentication, IUnauthenticatedPrincipal
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.app.security.interfaces import PrincipalLookupError
from zope.authentication.interfaces import IAuthentication, IUnauthenticatedPrincipal
from zope.authentication.interfaces import IUnauthenticatedPrincipal
from zope.authentication.interfaces import PrincipalLookupError
from zope.browser.interfaces import ITerms
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve
from zope.dublincore.interfaces import IZopeDublinCore
from zope.formlib import form
from zope.formlib.form import FormFields
from zope.formlib.namedtemplate import NamedTemplate
from zope.interface import Interface, implements
from zope.i18n.interfaces import ITranslationDomain
from zope.interface import Interface, implementer
from zope.proxy import removeAllProxies
from zope.publisher.browser import applySkin
from zope.publisher.interfaces.browser import IBrowserSkinType, IBrowserView
@ -98,16 +81,20 @@ class NameField(schema.ASCIILine):
class ViewMode(object):
def __init__(self, name='view', title=None, url=None, active=False,
description=u''):
description=u'', subViewModes=Jeep()):
self.name = name
self.title = title
self.url = url
self.active = active
self.description = description
self.subViewModes = subViewModes
@property
def cssClass(self):
return self.active and u'active' or u'inactive'
result = self.active and u'active' or u'inactive'
if self.subViewModes:
result += u' sub-modes'
return result
class IAddForm(Interface):
@ -192,9 +179,11 @@ class BaseView(GenericView, I18NView, SortableMixin):
actions = {}
portlet_actions = []
parts = ()
subparts = ()
icon = None
modeName = 'view'
isToplevel = False
isVisible = True
def __init__(self, context, request):
context = baseObject(context)
@ -203,6 +192,10 @@ class BaseView(GenericView, I18NView, SortableMixin):
self.context = removeSecurityProxy(context)
try:
if not self.checkPermissions():
logger = getLogger('loops.browser.common-153')
principal = request.principal and request.principal.id
msg = 'Unauthorized: %s, %s' % (self.contextInfo, principal)
logger.warn(msg)
raise Unauthorized(str(self.contextInfo))
except ForbiddenAttribute: # ignore when testing
pass
@ -215,6 +208,11 @@ class BaseView(GenericView, I18NView, SortableMixin):
def checkPermissions(self):
return canAccessObject(self.context)
def translate(self, text, msgFactory=_):
if msgFactory is None:
return text
return msgFactory(text)
@Lazy
def contextInfo(self):
return dict(view=self, context=getName(self.context))
@ -361,6 +359,9 @@ class BaseView(GenericView, I18NView, SortableMixin):
for c in cr:
try:
principal = pau.getPrincipal(c)
if principal is None:
creators.append(c)
else:
creators.append(principal.title)
except PrincipalLookupError:
creators.append(c)
@ -450,6 +451,10 @@ class BaseView(GenericView, I18NView, SortableMixin):
def description(self):
return self.adapted.description
@Lazy
def tabTitle(self):
return u'Info'
@Lazy
def additionalInfos(self):
return []
@ -674,6 +679,11 @@ class BaseView(GenericView, I18NView, SortableMixin):
def typeOptions(self):
if self.typeProvider is None:
return DummyOptions()
if getattr(self.adapted, '__is_dummy__', None):
typeToken = getattr(self, 'typeToken', None)
if typeToken is not None:
typeProvider = self.loopsRoot.loopsTraverse(typeToken)
return IOptions(adapted(typeProvider))
return IOptions(adapted(self.typeProvider))
@Lazy
@ -747,6 +757,9 @@ class BaseView(GenericView, I18NView, SortableMixin):
if not self.useVersioning:
return None
target = self.target
if not IResource.providedBy(target):
# no standard versioning yet for concepts
return None
versionable = IVersionable(target, None)
if versionable is None:
return ''
@ -861,6 +874,8 @@ class BaseView(GenericView, I18NView, SortableMixin):
def checkAction(self, name, category, target):
if name in ('create_resource',):
if target is not None and target.options.showCreateResource:
return True
return not self.globalOptions('hideCreateResource')
return True
@ -880,6 +895,7 @@ class BaseView(GenericView, I18NView, SortableMixin):
@Lazy
def xeditable(self):
return False
if self.typeOptions('no_external_edit'):
return False
ct = getattr(self.context, 'contentType', '')
@ -928,9 +944,9 @@ class BaseView(GenericView, I18NView, SortableMixin):
djConfig='parseOnLoad: true, usePlainJson: true, '
#'isDebug: true, '
'locale: "%s"' % self.languageInfo.language)
jsCall = ('dojo.require("dojo.parser"); '
'dojo.registerModulePath("jocy", "/@@/cybertools.jocy"); '
'dojo.require("jocy.data");')
jsCall = ('dojo.require("dojo.parser"); ')
#'dojo.registerModulePath("jocy", "/@@/cybertools.jocy"); '
#'dojo.require("jocy.data");')
cm.register('js-execute', 'dojo_registration', jsCall=jsCall)
cm.register('css', identifier='Lightbox.css', position=0,
resourceName='ajax.dojo/dojox/image/resources/Lightbox.css',
@ -1058,33 +1074,32 @@ class LoggedIn(object):
# vocabulary stuff
@implementer(ITerms)
class SimpleTerms(object):
""" Provide the ITerms interface, e.g. for usage in selection
lists.
"""
implements(ITerms)
def __init__(self, source, request):
# the source parameter is a list of tuples (token, title).
self.source = source
self.terms = dict(source)
def getTerm(self, value):
token, title = value
token = value[0]
title = len(value) > 1 and value[1] or token
return SimpleTerm(token, token, title)
def getValue(self, token):
return (token, self.terms[token])
@implementer(ITerms)
class LoopsTerms(object):
""" Provide the ITerms interface, e.g. for usage in selection
lists.
"""
implements(ITerms)
def __init__(self, source, request):
# the source parameter is a view or adapter of a real context object:
self.source = source
@ -1106,12 +1121,11 @@ class LoopsTerms(object):
return self.loopsRoot.loopsTraverse(token)
@implementer(ITerms)
class InterfaceTerms(object):
""" Provide the ITerms interface for source list of interfaces.
"""
implements(ITerms)
def __init__(self, source, request):
self.source = source
self.request = request

View file

@ -1,40 +1,22 @@
#
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.concept
"""
Definition of the concept view classes.
""" Definition of the concept view classes.
"""
from itertools import groupby
from zope import interface, component, schema
from zope.app.catalog.interfaces import ICatalog
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.container.contained import ObjectRemovedEvent
from zope.app.form.browser.interfaces import ITerms
from zope.app.form.interfaces import IDisplayWidget
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.authentication.interfaces import IUnauthenticatedPrincipal
from zope.browser.interfaces import ITerms
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.catalog.interfaces import ICatalog
from zope.container.contained import ObjectRemovedEvent
from zope.dottedname.resolve import resolve
from zope.event import notify
from zope.formlib.form import EditForm, FormFields, setUpEditWidgets
from zope.formlib.interfaces import IDisplayWidget
from zope.formlib.namedtemplate import NamedTemplate
from zope.interface import implements
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.publisher.interfaces import BadRequest
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.schema.interfaces import IIterableSource
@ -254,18 +236,35 @@ class ConceptView(BaseView):
result.append(view)
return result
def viewModes(self):
modes = Jeep()
current = self.request.form.get('loops.viewName')
parts = (self.options('view_tabs') or
self.typeOptions('view_tabs') or [])
if not parts:
return modes
activeMode = None
for p in parts:
view = component.queryMultiAdapter(
(self.adapted, self.request), name=p)
if view is None:
view = component.queryMultiAdapter(
(self.context, self.request), name=p)
if view is None:
continue
active = (activeMode is None and p == current)
if active:
activeMode = p
url = '%s?loops.viewName=%s' % (self.targetUrl, p)
modes.append(ViewMode(p, view.tabTitle, url, active))
if activeMode is None:
modes[0].active = True
return modes
@Lazy
def adapted(self):
return adapted(self.context, self.languageInfo)
@Lazy
def title(self):
return self.adapted.title or getName(self.context)
@Lazy
def description(self):
return self.adapted.description
@Lazy
def targetUrl(self):
return self.nodeView.getUrlForTarget(self.context)
@ -291,7 +290,7 @@ class ConceptView(BaseView):
def breadcrumbsParent(self):
for p in self.context.getParents([self.defaultPredicate]):
view = self.nodeView.getViewForTarget(p)
if view.showInBreadcrumbs:
if view is not None and view.showInBreadcrumbs:
return view
return None
@ -398,7 +397,8 @@ class ConceptView(BaseView):
children = getChildren
def childrenAlphaGroups(self, predicates=None):
result = Jeep()
#result = Jeep()
result = {}
rels = self.getChildren(predicates=predicates or [self.defaultPredicate],
topLevelOnly=False, sort=False)
rels = sorted(rels, key=lambda r: r.title.lower())
@ -445,7 +445,7 @@ class ConceptView(BaseView):
def parents(self):
rels = sorted(self.context.getParentRelations(),
key=(lambda x: x.first.title and x.first.title.lower()))
key=(lambda x: x.first.title and x.first.title.lower() or ''))
for r in rels:
yield self.childViewFactory(r, self.request)

View file

@ -10,7 +10,9 @@
<metal:data define-macro="conceptdata">
<div tal:attributes="class string:content-$level;">
<tal:block condition="not:title_shown|python:False">
<metal:block use-macro="view/concept_macros/concepttitle"/>
</tal:block>
<metal:slot define-slot="fields">
<metal:block use-macro="view/concept_macros/conceptfields" />
</metal:slot>
@ -51,7 +53,7 @@
<h1 tal:define="tabview item/tabview|nothing"
tal:attributes="ondblclick item/openEditWindow">
<a tal:omit-tag="python: level > 1"
tal:attributes="href string:${request/URL}${item/urlParamString}"
tal:attributes="href string:${view/requestUrl}${item/urlParamString}"
tal:content="item/title">Title</a>
<a title="Show tabular view"
i18n:attributes="title"
@ -87,9 +89,10 @@
fieldInstance python:field.getFieldInstance(
context=context, request=request);
rendererName field/displayRenderer;
renderer python:fieldInstance.getRenderer(rendererName) or
renderer python:
fieldInstance.getRenderer(rendererName) or
view.concept_macros[rendererName]"
tal:condition="nocall:value">
tal:condition="python:field.visible and (value or field.showEmpty)">
<td><b tal:content="field/title" i18n:translate="" />:</td>
<td><span metal:use-macro="renderer" /></td>
</tr>
@ -115,15 +118,6 @@
</metal:email>
<metal:email define-macro="display_relationset">
<span tal:repeat="elem value">
<a tal:attributes="href elem/url"
tal:content="elem/label"></a>
<tal:separator condition="not:repeat/elem/end"> | </tal:separator>
</span>
</metal:email>
<metal:parents define-macro="conceptparents">
<div tal:attributes="class string:content-$level;
ondblclick python: item.openEditWindow('configure.html')">
@ -191,14 +185,17 @@
tal:attributes="value related/uidToken" />
</td>
</tr>
<tr tal:define="children python:list(related.unique(related.children()));
<tal:nested condition="list_nested">
<tr tal:define="children python:
list(related.unique(related.children()));
resources python:list(related.resources())"
tal:condition="python:list_nested and (children or resources)">
tal:condition="python:children or resources">
<td tal:condition="item/showCheckboxes|nothing" />
<td colspan="5">
<metal:list use-macro="item/template/macros/list_nested" />
</td>
</tr>
</tal:nested>
</tal:item>
</tal:items>
</tbody>

View file

@ -34,16 +34,18 @@
<!-- login/logout -->
<page for="loops.interfaces.INode"
<!--<page for="loops.interfaces.INode"
name="login.html"
class="loops.browser.auth.LoginForm"
permission="zope.View" />
permission="zope.View" />-->
<page for="loops.interfaces.INode"
name="logout.html"
class="loops.browser.auth.Logout"
permission="zope.View" />
<!-- see also view/adapter "login.html" in section "query views" -->
<!-- macros -->
<page
@ -111,7 +113,7 @@
name="AddLoopsContainer.html"
for="zope.app.container.interfaces.IAdding"
class="loops.browser.manager.LoopsAddForm"
permission="zope.ManageApplication"
permission="zope.ManageSite"
/>
<addMenuItem
@ -123,7 +125,7 @@
<containerViews
for="loops.interfaces.ILoops"
index="zope.View"
index="zope.ManageSite"
contents="loops.ManageSite"
add="loops.ManageSite" />
@ -363,7 +365,7 @@
<containerViews
for="loops.interfaces.IViewManager"
index="zope.View"
index="zope.ManageSite"
add="loops.ManageSite" />
<menuItem
@ -537,6 +539,14 @@
<!-- query views -->
<!--<zope:adapter
name="login.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.browser.auth.LoginConcept"
permission="zope.View" />-->
<zope:adapter
name="list_children.html"
for="loops.interfaces.IConcept
@ -671,6 +681,13 @@
factory="loops.browser.form.EditConcept"
permission="zope.View" />
<page
name="test.html"
for="loops.interfaces.INode"
class="loops.browser.node.TestView"
permission="zope.View" />
<!-- inner HTML views -->
<page

View file

@ -1,28 +1,8 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.external
"""
view class(es) for import/export.
$Id$
""" view class(es) for import/export.
"""
from zope.interface import Interface, implements
from zope.app import zapi
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,35 +1,18 @@
#
# Copyright (c) 2014 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.form
"""
Classes for form presentation and processing.
""" Classes for form presentation and processing.
"""
from urllib import urlencode
from zope.app.container.contained import ObjectRemovedEvent
from urllib.parse import urlencode, unquote_plus
from zope import component, interface, schema
from zope.component import adapts
from zope.container.contained import ObjectRemovedEvent
from zope.event import notify
from zope.interface import Interface
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.container.interfaces import INameChooser
from zope.app.container.contained import ObjectAddedEvent
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.container.interfaces import INameChooser
from zope.lifecycleevent import ObjectAddedEvent
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.contenttype import guess_content_type
from zope.publisher.browser import FileUpload
@ -179,7 +162,14 @@ class ObjectForm(NodeView):
for k, v in data.items():
#overwrite data with values from request.form
if k in self.request.form:
data[k] = toUnicode(form[k])
field = self.schema.fields.get(k)
if field:
fi = field.getFieldInstance(self.instance)
input = form[k]
if isinstance(input, str):
input = unquote_plus(input)
data[k] = fi.marshall(fi.unmarshall(input))
#data[k] = toUnicode(form[k])
return data
@Lazy
@ -315,6 +305,7 @@ class CreateObjectForm(ObjectForm):
defaultTitle = u'Create Resource, Type = '
form_action = 'create_resource'
dialog_name = 'create'
setupTarget = False
@property
def macro(self): return self.template.macros['create']
@ -358,6 +349,7 @@ class CreateObjectForm(ObjectForm):
def adapted(self):
ad = self.typeInterface(Resource())
ad.storageName = 'unknown' # hack for file objects: don't try to retrieve data
ad.__is_dummy__ = True
ad.__type__ = adapted(self.typeConcept)
return ad
@ -725,11 +717,20 @@ class CreateObject(EditObject):
factory = Resource
defaultTypeToken = '.loops/concepts/textdocument'
namePrefix = u''
@Lazy
def container(self):
return self.loopsRoot.getResourceManager()
@Lazy
def objectType(self):
tc = self.request.form.get('form.type') or self.defaultTypeToken
return self.loopsRoot.loopsTraverse(tc)
def getName(self):
return None
def getNameFromData(self):
data = self.request.form.get('data')
if data and isinstance(data, FileUpload):
@ -748,12 +749,14 @@ class CreateObject(EditObject):
if not title:
raise BadRequest('Title field is empty')
obj = self.factory(title)
name = self.getName()
if name is None:
name = self.getNameFromData()
# TODO: validate fields
name = INameChooser(container).chooseName(name, obj)
nc = INameChooser(container)
nc.prefix = self.namePrefix
name = nc.chooseName(name, obj)
container[name] = obj
tc = form.get('form.type') or self.defaultTypeToken
obj.setType(self.loopsRoot.loopsTraverse(tc))
obj.setType(self.objectType)
notify(ObjectCreatedEvent(obj))
#notify(ObjectAddedEvent(obj))
self.object = self.view.object = obj
@ -808,8 +811,15 @@ class CreateConcept(EditConcept, CreateObject):
@Lazy
def container(self):
cmName = adapted(self.objectType).conceptManager
if cmName:
return self.loopsRoot[cmName]
return self.loopsRoot.getConceptManager()
@Lazy
def namePrefix(self):
return adapted(self.objectType).namePrefix or u''
def getNameFromData(self):
return None

18
browser/form_macros.pt → loops/browser/form_macros.pt Normal file → Executable file
View file

@ -264,9 +264,10 @@
<label style="display: inline"
for="version.create"><span i18n:translate="">
New version</span></label>
<tal:level condition="python: len(view.versionLevels) > 1">:
<tal:level define="versionLevels python:list(view.versionLevels)"
condition="python: len(versionLevels) > 1">:
<select name="version.level">
<option tal:repeat="level view/versionLevels"
<option tal:repeat="level versionLevels"
i18n:translate=""
tal:attributes="value level/token"
tal:content="level/label" />
@ -281,14 +282,18 @@
tal:define="states view/states"
tal:condition="states">
<tr><td colspan="5" i18n:translate="" class="headline">States</td></tr>
<metal:states define-macro="states_info">
<tr tal:repeat="st states">
<tal:state define="stObj st/getStateObject;
stDef st/statesDefinition;
stTrans st/getAvailableTransitionsForUser">
<td colspan="2" i18n:translate=""
tal:content="stDef">loops.simple_publishing</td>
<td i18n:translate=""
tal:content="stObj/title">draft</td>
<td colspan="2">
<b i18n:translate=""
tal:content="python:view.translate(stDef, st.msgFactory)">
loops.simple_publishing</b>:&nbsp;</td>
<td>
<span i18n:translate=""
tal:content="stObj/title">draft</span>&nbsp;</td>
<td>
<select name="state.loops.simple_publishing"
tal:condition="stTrans"
@ -304,6 +309,7 @@
</tal:state>
</tr>
</metal:states>
</metal:states>
<div metal:define-macro="buttons" class="buttons">

View file

@ -2,7 +2,7 @@
<metal:info define-macro="object_info"
tal:define="item nocall:view/item">
tal:define="item nocall:view/targetItem">
<table class="object_info" width="400">
<tr>
<td colspan="2"><h2 i18n:translate="">Object Information</h2><br /></td>
@ -52,7 +52,7 @@
<metal:info define-macro="meta_info"
tal:define="item nocall:view/item">
tal:define="item nocall:view/targetItem">
<table class="object_info" width="400">
<tr>
<td colspan="2">

View file

@ -1,26 +1,9 @@
#
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.lobo.standard
"""
View classes for lobo (blueprint-based) layouts.
""" View classes for lobo (blueprint-based) layouts.
"""
from cgi import parse_qs
from urllib.parse import parse_qs
from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy

View file

@ -1,7 +1,6 @@
# $Id$
# loops.browser.lobo.tests
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
from zope.interface.verify import verifyClass
class Test(unittest.TestCase):
@ -14,8 +13,8 @@ class Test(unittest.TestCase):
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags),
unittest.TestLoader().loadTestsFromTestCase(Test),
doctest.DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':

26
browser/loops.js → loops/browser/loops.js Normal file → Executable file
View file

@ -42,7 +42,7 @@ function showIf(node, value, targetName) {
function showIfIn(node, conditions) {
dojo.forEach(conditions, function(cond) {
var display = (node.value == cond[0]) ? 'inline' : 'none';
var display = (node.value == cond[0]) ? '' : 'none';
dojo.byId(cond[1]).style.display = display;
})
}
@ -56,6 +56,26 @@ function setIfIn(node, conditions) {
})
}
function setIf(node, cond, acts) {
if (node.value == cond) {
dojo.forEach(acts, function(act) {
target = dijit.byId(act[0]);
target.setValue(act[1]);
})
}
}
function setIfN(node, conds, acts) {
dojo.forEach(conds, function(cond) {
if (node.value == cond) {
dojo.forEach(acts, function(act) {
target = dijit.byId(act[0]);
target.setValue(act[1]);
})
}
})
}
function destroyWidgets(node) {
dojo.forEach(dojo.query('[widgetId]', node), function(n) {
w = dijit.byNode(n);
@ -112,7 +132,7 @@ function submitReplacing(targetId, formId, url) {
mimetype: "text/html",
load: function(response, ioArgs) {
replaceNode(response, targetId);
return resonse;
return response;
}
})
}
@ -124,7 +144,7 @@ function xhrSubmitPopup(formId, url) {
mimetype: "text/html",
load: function(response, ioArgs) {
window.close();
return resonse;
return response;
}
});
}

View file

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 942 B

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -22,7 +22,6 @@ Definition of view classes for the top-level loops container.
$Id$
"""
from zope.app import zapi
from zope import component
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.event import notify

View file

@ -1,31 +1,11 @@
#
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.mobile.default
"""
Default layouts for the loops mobile skin.
$Id$
""" Default layouts for the loops mobile skin.
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.interface import implements
from cybertools.browser.renderer import RendererFactory
from cybertools.composer.layout.base import Layout

168
browser/node.py → loops/browser/node.py Normal file → Executable file
View file

@ -1,42 +1,28 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.node
"""
View class for Node objects.
""" View class for Node objects.
"""
from urlparse import urlparse, urlunparse
from zope import component, interface, schema
from zope.cachedescriptors.property import Lazy
from zope.annotation.interfaces import IAnnotations
from zope.app.catalog.interfaces import ICatalog
from logging import getLogger
from urllib.parse import urlencode, urlparse, urlunparse
#from urlparse import urlparse, urlunparse
from zope.app.container.browser.contents import JustContents
from zope.app.container.browser.adding import Adding
from zope.app.container.traversal import ItemTraverser
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope import component, interface, schema
from zope.annotation.interfaces import IAnnotations
from zope.authentication.interfaces import IUnauthenticatedPrincipal
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.catalog.interfaces import ICatalog
from zope.container.traversal import ItemTraverser
from zope.dottedname.resolve import resolve
from zope.event import notify
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.lifecycleevent import Attributes
from zope.formlib.form import Form, FormFields
from zope.proxy import removeAllProxies
from zope.app.publisher.browser import getDefaultViewName
from zope.publisher.defaultview import getDefaultViewName
from zope.publisher.interfaces import NotFound
from zope.security import canAccess, canWrite, checkPermission
from zope.security.proxy import removeSecurityProxy
from zope.traversing.api import getParent, getParents, getPath
@ -65,6 +51,7 @@ from loops import util
from loops.util import _
from loops.versioning.util import getVersion
logger = getLogger('loops.browser.node')
node_macros = ViewPageTemplateFile('node_macros.pt')
info_macros = ViewPageTemplateFile('info.pt')
@ -76,26 +63,71 @@ class NodeView(BaseView):
_itemNum = 0
template = node_macros
nextUrl = None
setupTarget = True
def __init__(self, context, request):
super(NodeView, self).__init__(context, request)
self.viewAnnotations.setdefault('nodeView', self)
self.viewAnnotations.setdefault('node', self.context)
viewConfig = getViewConfiguration(context, request)
self.setSkin(viewConfig.get('skinName'))
self.setSkin(self.viewConfig.get('skinName'))
def __call__(self, *args, **kw):
if self.nodeType == 'raw':
vn = self.context.viewName
if vn:
self.request.response.setHeader('content-type', vn)
return self.context.body
tv = self.viewAnnotations.get('targetView')
if tv is not None:
if tv.isToplevel:
return tv(*args, **kw)
if self.controller is not None:
self.controller.setMainPage()
return super(NodeView, self).__call__(*args, **kw)
@Lazy
def viewConfig(self):
return getViewConfiguration(self.context, self.request)
@Lazy
def viewConfigOptions(self):
result = {}
for opt in self.viewConfig.get('options') or []:
if ':' in opt:
k, v = opt.split(':', 1)
result[k] = v.split(',')
else:
result[opt] = True
return result
@Lazy
def copyright(self):
cr = self.viewConfigOptions.get('copyright')
if cr:
return cr[0]
cr = self.globalOptions('copyright')
return cr and cr[0] or 'cyberconcepts.org team'
@Lazy
def macro(self):
return self.template.macros['content']
def update(self):
@Lazy
def subparts(self):
def getParts(n):
t = n.targetObjectView
if t is None:
return []
return t.subparts
parts = getParts(self)
#return parts
for n in self.textItems:
parts.extend(getParts(n))
return parts
def update(self, topLevel=True):
if topLevel and self.view != self:
return self.view.update(False)
result = super(NodeView, self).update()
self.recordAccess()
return result
@ -349,6 +381,10 @@ class NodeView(BaseView):
def editable(self):
return canWrite(self.context, 'body')
def hasTopPage(self, name):
page = self.topMenu.context.get(name)
return page is not None
# menu stuff
@Lazy
@ -394,8 +430,9 @@ class NodeView(BaseView):
@Lazy
def menuItems(self):
return [NodeView(child, self.request)
items = [NodeView(child, self.request).view
for child in self.context.getMenuItems()]
return [item for item in items if item.isVisible]
@Lazy
def parents(self):
@ -423,6 +460,15 @@ class NodeView(BaseView):
def active(self, item):
return item.context == self.context or item.context in self.parents
@Lazy
def logoutUrl(self):
nextUrl = urlencode(dict(nextUrl=self.menu.url))
return '%s/logout.html?%s' % (self.menu.url, nextUrl)
@Lazy
def authenticationMethod(self):
return self.viewAnnotations.get('auth_method') or 'standard'
# virtual target support
@Lazy
@ -498,7 +544,8 @@ class NodeView(BaseView):
if tv is not None:
tv.setupController()
return tv
return self.getViewForTarget(self.virtualTargetObject)
setup = self.setupTarget
return self.getViewForTarget(self.virtualTargetObject, setup=setup)
@Lazy
def targetId(self):
@ -534,7 +581,7 @@ class NodeView(BaseView):
@Lazy
def typeProvider(self):
if self.virtualTargetObject is not None:
return IType(self.virtualTargetObject).typeProvider
return IType(baseObject(self.virtualTargetObject)).typeProvider
return None
# target viewing and editing support
@ -543,24 +590,38 @@ class NodeView(BaseView):
""" Return URL of given target view given as .XXX URL.
"""
if isinstance(target, BaseView):
miu = self.getMenuItemUrlForTarget(target.context)
if miu is not None:
return miu
return self.makeTargetUrl(self.url, target.uniqueId, target.title)
else:
target = baseObject(target)
return self.makeTargetUrl(self.url, util.getUidForObject(target),
target.title)
def getMenuItemUrlForTarget(self, tobj):
for node in tobj.getClients():
if node.nodeType == 'page' and node.getMenu() == self.menuObject:
return absoluteURL(node, self.request)
def getActions(self, category='object', page=None, target=None):
actions = []
#self.registerDojo()
self.registerDojoFormAll()
if target is None:
target = self.virtualTarget
#target = self.getViewForTarget(self.virtualTargetObject,
# setup=False)
if category in self.actions:
actions.extend(self.actions[category](self, target=target))
if target is not None:
actions.extend(target.getActions(category, page=self, target=target))
if target != self.virtualTarget: # self view must be used directly for target
actions.extend(self.view.getAdditionalActions(category, self, target))
if target is not None and self.setupTarget:
actions.extend(target.getActions(
category, page=self, target=target))
if target is not None and target.context != self.virtualTargetObject:
# self view must be used directly for target
actions.extend(self.view.getAdditionalActions(
category, self, target))
return actions
def getPortletActions(self, target=None):
@ -582,7 +643,6 @@ class NodeView(BaseView):
actions = dict(portlet=getPortletActions)
def checkAction(self, name, category, target):
#if name in ('create_resource',) and target is not None:
if target is not None:
return target.checkAction(name, category, target)
return super(NodeView, self).checkAction(name, category, target)
@ -709,11 +769,11 @@ class InlineEdit(NodeView):
if ti is not None:
target = ti(target)
data = self.request.form['editorContent']
if type(data) != unicode:
if not isinstance(data, str):
try:
data = data.decode('ISO-8859-15') # IE hack
except UnicodeDecodeError:
print 'loops.browser.node.InlineEdit.save():', data
print('loops.browser.node.InlineEdit.save():', data)
return
# data = data.decode('UTF-8')
target.data = data
@ -885,9 +945,9 @@ class NodeAdding(Adding):
return info
@interface.implementer(IViewConfiguratorSchema)
class ViewPropertiesConfigurator(object):
interface.implements(IViewConfiguratorSchema)
component.adapts(INode)
def __init__(self, context):
@ -973,7 +1033,12 @@ class NodeTraverser(ItemTraverser):
viewAnnotations['targetView'] = view
view.logInfo('NodeTraverser:targetView = %r' % view)
return self.context
try:
obj = super(NodeTraverser, self).publishTraverse(request, name)
except NotFound:
logger.warn('NodeTraverser: NotFound: URL = %s, name = %r' %
(request.URL, name))
raise
return obj
def getTargetUid(self, request):
@ -1021,7 +1086,9 @@ def setViewConfiguration(context, request):
config = IViewConfiguratorSchema(context)
skinName = config.skinName
if not skinName:
skinName = context.getLoopsRoot().skinName
root = removeSecurityProxy(context.getLoopsRoot())
skinName = root.skinName
#skinName = context.getLoopsRoot().skinName
if skinName:
viewAnnotations['skinName'] = skinName
if config.options:
@ -1034,3 +1101,18 @@ def getViewConfiguration(context, request):
viewAnnotations = request.annotations.get('loops.view', {})
return dict(skinName=viewAnnotations.get('skinName'),
options=viewAnnotations.get('options'))
class TestView(NodeView):
def __call__(self):
print( '*** begin')
for i in range(500):
#x = util.getObjectForUid('1994729849')
x = util.getObjectForUid('2018653366')
self.c = list(x.getChildren())
#self.c = list(x.getChildren([self.defaultPredicate]))
print('*** end', len(self.c))
return 'done'

View file

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 942 B

View file

@ -276,6 +276,17 @@
title mode/description"
tal:content="mode/title"
i18n:translate="" />
<ul class="sub-view-modes"
tal:define="subModes mode/subViewModes|nothing"
tal:condition="subModes">
<li tal:repeat="mode subModes"
tal:attributes="class mode/cssClass">
<a tal:attributes="href mode/url;
title mode/description;"
tal:content="mode/title"
i18n:translate=""></a>
</li>
</ul>
</li>
</ul>
</metal:actions>
@ -287,7 +298,8 @@
tal:condition="python: 'print' in pageActions"
title="Print this page"
i18n:attributes="title">
<a href="javascript:print()">
<a href="#"
onclick="print(); return false;">
<img tal:attributes="src string:$resourceBase/cybertools.icons/printer.png"
i18n:attributes="alt"
alt="Print" />
@ -321,18 +333,18 @@
<metal:login define-macro="login">
<div>
<a href="login.html"
tal:attributes="href string:${view/topMenu/url}/login.html"
i18n:translate="">Log in</a></div>
<div tal:define="register python:view.globalOptions('provideLogin')"
tal:condition="register">
<a tal:condition="python:register != True"
tal:attributes="href python:register[0]"
tal:condition="python:register and register != True">
<a tal:define="reg python:register[0]"
tal:attributes="href string:${view/topMenu/url}/$reg"
i18n:translate="">Register new member</a></div>
</metal:login>
<metal:actions define-macro="personal">
<div><a href="logout.html?nextURL=login.html"
tal:attributes="href string:logout.html?nextURL=${view/menu/url}"
<div><a tal:attributes="href view/logoutUrl"
i18n:translate="">Log out</a></div>
<tal:actions repeat="action python:view.getAllowedActions('personal')">
<metal:action use-macro="action/macro" />

View file

@ -18,10 +18,6 @@
<a href="#"
tal:attributes="href string:${target/url}/@@configure.html"
tal:content="target/title">Document xy</a>
<tal:xedit define="xeditObjectUrl target/url"
condition="target/xeditable">
<metal:xedit use-macro="views/xedit_macros/editLink" />
</tal:xedit>
</tal:target>
</div>

View file

@ -52,7 +52,7 @@
tal:attributes="value item/token" />
</td>
<td>
<a tal:content="item/title"
<a tal:content="item/title|string:???"
tal:attributes="href string:${item/url}/@@SelectedManagementView.html">
Title
</a>

View file

@ -1,36 +1,18 @@
#
# Copyright (c) 2015 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# loops.browser.resource
"""
View class for resource objects.
""" View class for resource objects.
"""
import os.path
import urllib
from zope.authentication.interfaces import IUnauthenticatedPrincipal
from zope.browserpage import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.app.catalog.interfaces import ICatalog
from zope.app.container.interfaces import INameChooser
from zope.app.form.browser.textwidgets import FileWidget
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.catalog.interfaces import ICatalog
from zope.container.interfaces import INameChooser
from zope.formlib.form import FormFields
from zope.formlib.interfaces import DISPLAY_UNWRITEABLE
from zope.formlib.textwidgets import FileWidget
from zope.proxy import removeAllProxies
from zope.schema.interfaces import IBytes
from zope.security import canAccess, canWrite
@ -189,7 +171,7 @@ class ResourceView(BaseView):
return DocumentView(context, self.request)
return self
def show(self, useAttachment=False):
def show(self, useAttachment=False, filename=None):
""" show means: "download"..."""
# TODO: control access, e.g. to protected images
# if self.adapted.isProtected():
@ -197,6 +179,9 @@ class ResourceView(BaseView):
context = self.context
ct = context.contentType
response = self.request.response
if self.typeOptions('x_robots_tag_header', None) is not None:
tagVal = ', '.join(self.typeOptions('x_robots_tag_header'))
response.setHeader('X-Robots-Tag', tagVal)
self.recordAccess('show', target=self.uniqueId)
if ct.startswith('image/'):
#response.setHeader('Cache-Control', 'public,max-age=86400')
@ -206,12 +191,17 @@ class ResourceView(BaseView):
from loops.media.browser.asset import MediaAssetView
view = MediaAssetView(context, self.request)
return view.show(useAttachment)
else:
response.setHeader('Cache-Control', '')
response.setHeader('Pragma', '')
ti = IType(context).typeInterface
if ti is not None:
context = ti(context)
data = context.data
if useAttachment:
filename = adapted(self.context).localFilename or getName(self.context)
if filename is None:
filename = (adapted(self.context).localFilename or
getName(self.context))
if self.typeOptions('use_title_for_download_filename'):
base, ext = os.path.splitext(filename)
filename = context.title
@ -264,15 +254,21 @@ class ResourceView(BaseView):
#wp = wiki.createPage(getName(self.context))
wp = wiki.addPage(LoopsWikiPage(self.context))
wp.text = text
#print wp.wiki.getManager()
#print(wp.wiki.getManager())
#return util.toUnicode(wp.render(self.request))
return super(ResourceView, self).renderText(text, contentType)
showMore = True
def renderShortText(self):
return self.renderDescription() or self.createShortText(self.render())
def createShortText(self, text=None):
return extractFirstPart(text or self.render())
text = (text or self.render()).strip()
shortText = extractFirstPart(text)
if shortText == text:
self.showMore = False
return shortText
def download(self):
""" Force download, e.g. of a PDF file """
@ -453,7 +449,7 @@ class ExternalEditorView(ExternalEditorView, BaseView):
r.append('meta_type:' + '.'.join((context.__module__, context.__class__.__name__)))
auth = self.request.get('_auth')
if auth:
print 'ExternalEditorView: auth = ', auth
print('ExternalEditorView: auth = ', auth)
if auth.endswith('\n'):
auth = auth[:-1]
r.append('auth:' + auth)
@ -477,4 +473,3 @@ class NoteView(DocumentView):
def linkUrl(self):
ad = self.typeAdapter
return ad and ad.linkUrl or ''

View file

@ -10,7 +10,7 @@
<div metal:use-macro="views/node_macros/object_actions" />
</tal:actions>
<h1><a tal:omit-tag="python: level > 1"
tal:attributes="href request/URL"
tal:attributes="href view/requestUrl"
tal:content="item/title">Title</a></h1>
<tal:desc define="description description|item/renderedDescription"
condition="description">
@ -51,7 +51,7 @@
<div tal:attributes="ondblclick python: item.openEditWindow('edit.html')">
<div metal:use-macro="views/node_macros/object_actions" />
<h1><a tal:omit-tag="python: level > 1"
tal:attributes="href request/URL"
tal:attributes="href view/requestUrl"
tal:content="item/title">Title</a></h1><br />
<img tal:attributes="src
string:${view/url}/.${view/targetId}/view?version=this" />
@ -72,6 +72,7 @@
<div>
<span class="button">
<a i18n:translate=""
target="_blank"
tal:attributes="href
string:${view/virtualTargetUrl}/download.html?version=this">
Download

View file

@ -1,6 +1,4 @@
"""
$Id$
"""
# package loops.browser.skin
from cybertools.browser.liquid import Liquid
from cybertools.browser.blue import Blue

View file

@ -18,6 +18,11 @@
permission="zope.View"
layer="loops.browser.skin.Loopy" />
<page for="*"
name="loopy.body_macros"
template="loopy/body.pt"
permission="zope.Public" />
<resource name="loops.css" file="loopy/loops.css"
layer="loops.browser.skin.Loopy" />
<resource name="custom.css" file="loopy/custom.css"

Some files were not shown because too many files have changed in this diff Show more