From fbd036a7d0da1d0884d8d470a3fdff5dc176a505 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 6 Apr 2013 16:13:06 +0200 Subject: [PATCH 1/7] provide button for resetting responses entered --- browser/loops.js | 8 +++++++- knowledge/survey/view_macros.pt | 3 +++ locales/de/LC_MESSAGES/loops.mo | Bin 23626 -> 23689 bytes locales/de/LC_MESSAGES/loops.po | 5 ++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/browser/loops.js b/browser/loops.js index 4be35a5..bb24e2c 100644 --- a/browser/loops.js +++ b/browser/loops.js @@ -1,4 +1,4 @@ -/* $Id$ */ +/* loops.js */ function openEditWindow(url) { zmi = window.open(url, 'zmi'); @@ -19,6 +19,12 @@ function toggleCheckBoxes(toggle, fieldName) { for (i in w) w[i].checked=toggle.checked; } +function setRadioButtons(value) { + dojo.forEach(dojo.query('input[type="radio"][value="' + value + '"]'), + function(n) { + n.checked = true;}) +} + function validate(nodeName, required) { // (work in progress) - may be used for onBlur event handler var w = dojo.byId(nodeName); diff --git a/knowledge/survey/view_macros.pt b/knowledge/survey/view_macros.pt index 49731dc..51c2c6a 100644 --- a/knowledge/survey/view_macros.pt +++ b/knowledge/survey/view_macros.pt @@ -85,6 +85,9 @@ + diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index c2a1970b3b85bb688dcd8ffcd2de2df37f39fd3c..9ebc458fe70403c8fea5c10820767a09f829c035 100644 GIT binary patch delta 8158 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 s#Bid3*hc7SPo2A;=gAjrS)Mc@WJ~>~Cj\n" "Language-Team: loops developers \n" "MIME-Version: 1.0\n" @@ -241,6 +241,9 @@ msgstr "Trifft für unser Unternehmen voll und ganz zu" msgid "Evaluate Questionnaire" msgstr "Fragebogen auswerten" +msgid "Reset Responses Entered" +msgstr "Eingaben zurücksetzen" + msgid "Back to Questionnaire" msgstr "Zurück zum Fragebogen" From a78ce4f8800a6b64a3ee5c20c9e357068a68b301 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 9 Apr 2013 08:38:02 +0200 Subject: [PATCH 2/7] work in progress: states portlet --- browser/node.py | 6 +++++- organize/stateful/browser.py | 10 ++++++++++ organize/stateful/view_macros.pt | 28 ++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/browser/node.py b/browser/node.py index 2763ada..270f459 100644 --- a/browser/node.py +++ b/browser/node.py @@ -195,7 +195,11 @@ class NodeView(BaseView): subMacro=calendar_macros.macros['main'], priority=90) # force early portlet registrations by target by setting up target view - self.virtualTarget + if self.virtualTarget is not None: + std = self.virtualTarget.typeOptions('portlet_states') + if std: + from loops.organize.stateful.browser import registerStatesPortlet + registerStatesPortlet(self.controller, self.virtualTarget, std) @Lazy def usersPresent(self): diff --git a/organize/stateful/browser.py b/organize/stateful/browser.py index ae57c87..42e04a2 100644 --- a/organize/stateful/browser.py +++ b/organize/stateful/browser.py @@ -43,6 +43,16 @@ statefulActions = ('classification_quality', 'publishable_task',) +def registerStatesPortlet(controller, view, statesDefs, + region='portlet_right', priority=98): + cm = controller.macros + stfs = [component.getAdapter(view.context, IStateful, name=std) + for std in statesDefs] + cm.register(region, 'states', title=_(u'States'), + subMacro=template.macros['portlet_states'], + priority=priority, info=view, stfs=stfs) + + class StateAction(Action): url = None diff --git a/organize/stateful/view_macros.pt b/organize/stateful/view_macros.pt index 6dfad17..d1be431 100644 --- a/organize/stateful/view_macros.pt +++ b/organize/stateful/view_macros.pt @@ -68,4 +68,32 @@ + + + +
+
+ Workflow + +
+
+ State: + +
+
+
Available Transitions:
+ +
+
+
+ + From cad21c5dc3218840a9b128d6220815dea4292b2b Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Thu, 11 Apr 2013 08:28:19 +0200 Subject: [PATCH 3/7] unauthenticated user should not provide a person ID --- knowledge/survey/browser.py | 1 + knowledge/survey/response.py | 7 +++---- organize/tracking/base.py | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/knowledge/survey/browser.py b/knowledge/survey/browser.py index 6b6c767..55935ad 100644 --- a/knowledge/survey/browser.py +++ b/knowledge/survey/browser.py @@ -48,6 +48,7 @@ class SurveyView(ConceptView): @Lazy def macro(self): + self.registerDojo() return template.macros['survey'] def results(self): diff --git a/knowledge/survey/response.py b/knowledge/survey/response.py index b27979e..3841c4f 100644 --- a/knowledge/survey/response.py +++ b/knowledge/survey/response.py @@ -39,10 +39,9 @@ class Responses(BaseRecordManager): self.context = context def save(self, data): - if not self.personId: - return - self.storage.saveUserTrack(self.uid, 0, self.personId, data, - update=True, overwrite=True) + if self.personId: + self.storage.saveUserTrack(self.uid, 0, self.personId, data, + update=True, overwrite=True) def load(self): if self.personId: diff --git a/organize/tracking/base.py b/organize/tracking/base.py index f8a1323..645e091 100644 --- a/organize/tracking/base.py +++ b/organize/tracking/base.py @@ -20,6 +20,7 @@ Base class(es) for track/record managers. """ +from zope.app.security.interfaces import IUnauthenticatedPrincipal from zope.cachedescriptors.property import Lazy from cybertools.meta.interfaces import IOptions @@ -65,6 +66,8 @@ class BaseRecordManager(object): else: principal = getPrincipalForUserId(userId, context=self.context) if principal is not None: + if IUnauthenticatedPrincipal.providedBy(principal): + return None person = getPersonForUser(self.context, principal=principal) if person is None: return principal.id From feb6ea1a060c3189ca0baddd0ef73606709cfd8e Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 15 Apr 2013 07:50:17 +0200 Subject: [PATCH 4/7] work in progress: self registration with email notiification/confirmation --- organize/browser/configure.zcml | 12 +++++++++++ organize/browser/member.py | 35 +++++++++++++++++++++++++++------ organize/browser/view_macros.pt | 4 ++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/organize/browser/configure.zcml b/organize/browser/configure.zcml index 91d40f1..e351efa 100644 --- a/organize/browser/configure.zcml +++ b/organize/browser/configure.zcml @@ -27,6 +27,18 @@ class="loops.organize.browser.member.MemberRegistration" permission="zope.View" /> + + + + + + + + From 4a6d5abefcfb27e2749a43eeb8643eff8741e2e3 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 23 Apr 2013 08:27:52 +0200 Subject: [PATCH 5/7] don't show lightbox if image is large --- browser/lobo/standard.pt | 1 + layout/base.py | 1 + 2 files changed, 2 insertions(+) diff --git a/browser/lobo/standard.pt b/browser/lobo/standard.pt index 9148b61..34a015e 100644 --- a/browser/lobo/standard.pt +++ b/browser/lobo/standard.pt @@ -117,6 +117,7 @@ Date: Fri, 26 Apr 2013 09:23:25 +0200 Subject: [PATCH 6/7] fix check for showing tabview icon --- knowledge/survey/browser.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/knowledge/survey/browser.py b/knowledge/survey/browser.py index 55935ad..99eac6d 100644 --- a/knowledge/survey/browser.py +++ b/knowledge/survey/browser.py @@ -42,7 +42,6 @@ template = ViewPageTemplateFile('view_macros.pt') class SurveyView(ConceptView): - tabview = 'index.html' data = None errors = None @@ -51,6 +50,11 @@ class SurveyView(ConceptView): self.registerDojo() return template.macros['survey'] + @Lazy + def tabview(self): + if self.editable: + return 'index.html' + def results(self): result = [] response = None From 320e83c602c5b9a630a4926bc3d64cf20cb03733 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 27 Apr 2013 15:07:11 +0200 Subject: [PATCH 7/7] provide tabbed page concept type for showing sub-queries as tabs --- browser/concept.py | 30 +++++++++++++++++++++++++++++- browser/configure.zcml | 8 ++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/browser/concept.py b/browser/concept.py index c94dce3..b550830 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2012 Helmut Merz helmutm@cy55.de +# 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 @@ -50,6 +50,7 @@ from cybertools.meta.interfaces import IOptions from cybertools.typology.interfaces import IType, ITypeManager from cybertools.util.jeep import Jeep from loops.browser.common import EditForm, BaseView, LoopsTerms, concept_macros +from loops.browser.common import ViewMode from loops.common import adapted from loops.concept import Concept, ConceptTypeSourceList, PredicateSourceList from loops.i18n.browser import I18NView @@ -746,3 +747,30 @@ class ListTypeInstances(ListChildren): noDuplicates, useFilter, [self.typePredicate]): yield c + +class TabbedPage(ConceptView): + + @Lazy + def subpagePredicates(self): + pred = self.conceptManager.get('issubpage') + if pred is None: + pred = self.isPartOfPredicate + return [pred] + + def viewModes(self): + modes = Jeep() + for s in self.getSiblings(self.subpagePredicates): + url = self.nodeView.getUrlForTarget(s) + modes.append(ViewMode(getName(s), s.title, url)) + if not modes: + return modes + modes[getName(self.context)].active = True + return modes + + def getSiblings(self, preds): + for p in self.context.getParents(preds): + parent = p + break + else: + return [] + return p.getChildren(preds) diff --git a/browser/configure.zcml b/browser/configure.zcml index f6f3695..70f3b2c 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -553,6 +553,14 @@ factory="loops.browser.concept.ListTypeInstances" permission="zope.View" /> + +