Merge branch 'bbmaster' of ssh://git.cy55.de/home/git/loops into bbmaster
This commit is contained in:
		
						commit
						531252cc88
					
				
					 31 changed files with 577 additions and 87 deletions
				
			
		|  | @ -123,6 +123,15 @@ actions.register('external_edit', 'object', TargetAction, | ||||||
|         cssClass='icon-action', |         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, | actions.register('edit_object', 'portlet', DialogAction, | ||||||
|         title=_(u'Edit Resource...'), |         title=_(u'Edit Resource...'), | ||||||
|         description=_(u'Modify resource object.'), |         description=_(u'Modify resource object.'), | ||||||
|  |  | ||||||
|  | @ -489,7 +489,7 @@ class BaseView(GenericView, I18NView): | ||||||
|             return absoluteURL(provider, self.request) |             return absoluteURL(provider, self.request) | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     def renderText(self, text, contentType): |     def renderText(self, text, contentType='text/restructured'): | ||||||
|         text = util.toUnicode(text) |         text = util.toUnicode(text) | ||||||
|         typeKey = util.renderingFactories.get(contentType, None) |         typeKey = util.renderingFactories.get(contentType, None) | ||||||
|         if typeKey is None: |         if typeKey is None: | ||||||
|  |  | ||||||
|  | @ -51,6 +51,8 @@ | ||||||
|           </th> |           </th> | ||||||
|         </tr></tbody> |         </tr></tbody> | ||||||
| 
 | 
 | ||||||
|  |         <tbody metal:define-slot="custom_header" /> | ||||||
|  | 
 | ||||||
|         <tbody><tr><td colspan="5" style="padding-right: 15px"> |         <tbody><tr><td colspan="5" style="padding-right: 15px"> | ||||||
|               <div id="form.fields"> |               <div id="form.fields"> | ||||||
|                 <metal:fields use-macro="view/fieldRenderers/fields" /> |                 <metal:fields use-macro="view/fieldRenderers/fields" /> | ||||||
|  | @ -59,7 +61,7 @@ | ||||||
| 
 | 
 | ||||||
|         <tbody> |         <tbody> | ||||||
|           <tr metal:use-macro="view/template/macros/assignments" /> |           <tr metal:use-macro="view/template/macros/assignments" /> | ||||||
|           <tal:custom define="customMacro view/customMacro" |           <tal:custom define="customMacro view/customMacro|nothing" | ||||||
|                       condition="customMacro"> |                       condition="customMacro"> | ||||||
|             <tr metal:use-macro="customMacro" /> |             <tr metal:use-macro="customMacro" /> | ||||||
|           </tal:custom> |           </tal:custom> | ||||||
|  | @ -119,6 +121,8 @@ | ||||||
|                      tal:attributes="value typeToken" /> |                      tal:attributes="value typeToken" /> | ||||||
|         </th></tr></tbody> |         </th></tr></tbody> | ||||||
| 
 | 
 | ||||||
|  |         <tbody metal:define-slot="custom_header" /> | ||||||
|  | 
 | ||||||
|         <tbody><tr><td colspan="5"> |         <tbody><tr><td colspan="5"> | ||||||
|               <div id="form.fields"> |               <div id="form.fields"> | ||||||
|                 <metal:fields use-macro="view/fieldRenderers/fields" /> |                 <metal:fields use-macro="view/fieldRenderers/fields" /> | ||||||
|  | @ -127,7 +131,7 @@ | ||||||
| 
 | 
 | ||||||
|         <tbody> |         <tbody> | ||||||
|           <tr metal:use-macro="view/template/macros/assignments" /> |           <tr metal:use-macro="view/template/macros/assignments" /> | ||||||
|           <tal:custom define="customMacro view/customMacro" |           <tal:custom define="customMacro view/customMacro|nothing" | ||||||
|                       condition="customMacro"> |                       condition="customMacro"> | ||||||
|             <tr metal:use-macro="customMacro" /> |             <tr metal:use-macro="customMacro" /> | ||||||
|           </tal:custom> |           </tal:custom> | ||||||
|  |  | ||||||
|  | @ -59,6 +59,15 @@ textarea { | ||||||
|     margin-top: 0.5em; |     margin-top: 0.5em; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* elements taken from blueprint (Lobo) skin */ | ||||||
|  | .span-1, .span-2, .span-3, .span-4, .span-5, .span-6 {float: left;} | ||||||
|  | .span-1 {width: 105px;} | ||||||
|  | .span-2 {width: 230px;} | ||||||
|  | .span-3 {width: 355px;} | ||||||
|  | .span-4 {width: 480px;} | ||||||
|  | .span-5 {width: 605px;} | ||||||
|  | .span-6 {width: 730px;} | ||||||
|  | 
 | ||||||
| table.listing { | table.listing { | ||||||
|     margin: 1px; |     margin: 1px; | ||||||
|     margin-top: 6px; |     margin-top: 6px; | ||||||
|  |  | ||||||
|  | @ -386,6 +386,8 @@ class NodeView(BaseView): | ||||||
|             ht = super(NodeView, self).headTitle |             ht = super(NodeView, self).headTitle | ||||||
|             if ht not in parts: |             if ht not in parts: | ||||||
|                 parts.append(ht) |                 parts.append(ht) | ||||||
|  |         if self.globalOptions('reverseHeadTitle'): | ||||||
|  |             parts.reverse() | ||||||
|         return ' - ' .join(parts) |         return ' - ' .join(parts) | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|  |  | ||||||
|  | @ -314,8 +314,13 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <metal:login define-macro="login"> | <metal:login define-macro="login"> | ||||||
|     <div><a href="login.html" |     <div> | ||||||
|  |       <a href="login.html" | ||||||
|             i18n:translate="">Log in</a></div> |             i18n:translate="">Log in</a></div> | ||||||
|  |     <div tal:define="register python:view.globalOptions('provideLogin')" | ||||||
|  |          tal:condition="register"> | ||||||
|  |       <a tal:attributes="href python:register[0]" | ||||||
|  |          i18n:translate="">Register new member</a></div> | ||||||
| </metal:login> | </metal:login> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -30,7 +30,13 @@ | ||||||
|         <metal:tabs use-macro="views/node_macros/breadcrumbs" /> |         <metal:tabs use-macro="views/node_macros/breadcrumbs" /> | ||||||
|       </metal:breadcrumbs> |       </metal:breadcrumbs> | ||||||
|       <div metal:define-slot="actions"></div> |       <div metal:define-slot="actions"></div> | ||||||
|       <div metal:define-slot="message"></div> |       <metal:message define-slot="message"> | ||||||
|  |         <div class="message" | ||||||
|  |              i18n:translate="" | ||||||
|  |              tal:define="msg request/loops.message|nothing" | ||||||
|  |              tal:condition="msg" | ||||||
|  |              tal:content="msg" /> | ||||||
|  |       </metal:message> | ||||||
|       <metal:tabs use-macro="views/node_macros/view_modes" /> |       <metal:tabs use-macro="views/node_macros/view_modes" /> | ||||||
|       <metal:content define-slot="content"> |       <metal:content define-slot="content"> | ||||||
|         <tal:content define="item nocall:view/item; |         <tal:content define="item nocall:view/item; | ||||||
|  |  | ||||||
|  | @ -62,6 +62,10 @@ h1, h2, h3, h4, h5, h6 { | ||||||
|     margin-bottom: 0.4em; |     margin-bottom: 0.4em; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | a { | ||||||
|  |     text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| a[href]:hover { | a[href]:hover { | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     color: #6060c0; |     color: #6060c0; | ||||||
|  | @ -120,6 +124,10 @@ thead th { | ||||||
|     margin-bottom: 0.3em; |     margin-bottom: 0.3em; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .infotext { | ||||||
|  |     font-size: 90%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .fields td { | .fields td { | ||||||
|     vertical-align: top; |     vertical-align: top; | ||||||
| } | } | ||||||
|  | @ -163,6 +171,10 @@ table.listing td { | ||||||
|     border-bottom: 1px dotted #dddddd; |     border-bottom: 1px dotted #dddddd; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | table.listing tr.vpad td { | ||||||
|  |     padding: 7px 2px 7px 2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fieldset.box table.listing td { | fieldset.box table.listing td { | ||||||
|     padding: 0 1px 0 1px; |     padding: 0 1px 0 1px; | ||||||
| } | } | ||||||
|  | @ -267,7 +279,8 @@ fieldset.box td { | ||||||
| 
 | 
 | ||||||
| .top-actions { | .top-actions { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     top: 30px; |     top: 40px; | ||||||
|  |     margin-left: 10px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .quicksearch input { | .quicksearch input { | ||||||
|  | @ -281,7 +294,7 @@ fieldset.box td { | ||||||
| 
 | 
 | ||||||
| .page-actions { | .page-actions { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     top: 55px; |     top: 75px; | ||||||
|     margin-left: 210px; |     margin-left: 210px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -82,6 +82,15 @@ table { | ||||||
|     margin-top: 0.5em; |     margin-top: 0.5em; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* elements taken from blueprint (Lobo) skin */ | ||||||
|  | .span-1, .span-2, .span-3, .span-4, .span-5, .span-6 {float: left;} | ||||||
|  | .span-1 {width: 105px;} | ||||||
|  | .span-2 {width: 230px;} | ||||||
|  | .span-3 {width: 355px;} | ||||||
|  | .span-4 {width: 480px;} | ||||||
|  | .span-5 {width: 605px;} | ||||||
|  | .span-6 {width: 730px;} | ||||||
|  | 
 | ||||||
| tr.even td { | tr.even td { | ||||||
|     background-color: transparent; |     background-color: transparent; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.traversing.api import getName | from zope.traversing.api import getName | ||||||
| 
 | 
 | ||||||
|  | from cybertools.meta.interfaces import IOptions | ||||||
| from cybertools.typology.interfaces import IType | from cybertools.typology.interfaces import IType | ||||||
| from loops.browser.lobo import standard | from loops.browser.lobo import standard | ||||||
| from loops.browser.concept import ConceptView | from loops.browser.concept import ConceptView | ||||||
|  | @ -118,11 +119,36 @@ class Base(object): | ||||||
|                 self.images[idx].append(img) |                 self.images[idx].append(img) | ||||||
|         return result |         return result | ||||||
| 
 | 
 | ||||||
|     def getCssClassForResource(self, r): |     def getDocumentTypeForResource(self, r): | ||||||
|         for c in r.context.getConcepts([self.defaultPredicate]): |         for c in r.context.getConcepts([self.defaultPredicate]): | ||||||
|             if c.conceptType == self.documentTypeType: |             if c.conceptType == self.documentTypeType: | ||||||
|                 return getName(c) |                 return c | ||||||
|  | 
 | ||||||
|  |     def getOptionsForResource(self, r, name): | ||||||
|  |         dt = self.getDocumentTypeForResource(r) | ||||||
|  |         if dt is not None: | ||||||
|  |             return IOptions(adapted(dt))(name) | ||||||
|  | 
 | ||||||
|  |     def getTitleForResource(self, r): | ||||||
|  |         if self.getOptionsForResource(r, 'showtitle'): | ||||||
|  |             return r.title | ||||||
|  | 
 | ||||||
|  |     def getIconForResource(self, r): | ||||||
|  |         icon = self.getOptionsForResource(r, 'icon') | ||||||
|  |         if icon: | ||||||
|  |             return '/'.join((self.controller.resourceBase, icon[0])) | ||||||
|  | 
 | ||||||
|  |     def getCssClassForResource(self, r): | ||||||
|  |         dt = self.getDocumentTypeForResource(r) | ||||||
|  |         if dt is None: | ||||||
|             return 'textelement' |             return 'textelement' | ||||||
|  |         css = IOptions(adapted(dt))('cssclass') | ||||||
|  |         if css: | ||||||
|  |             return css | ||||||
|  |         return getName(dt) | ||||||
|  | 
 | ||||||
|  |     def getMacroForResource(self, r): | ||||||
|  |         return self.book_macros['default_text'] | ||||||
| 
 | 
 | ||||||
|     def getParentsForResource(self, r): |     def getParentsForResource(self, r): | ||||||
|         for c in r.context.getConcepts([self.defaultPredicate]): |         for c in r.context.getConcepts([self.defaultPredicate]): | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
|   <zope:adapter |   <zope:adapter | ||||||
|         name="book_overview" |         name="book_overview" | ||||||
|         for="loops.interfaces.IConcept |         for="loops.interfaces.IConcept | ||||||
|              loops.browser.skin.Lobo" |              zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|         provides="zope.interface.Interface" |         provides="zope.interface.Interface" | ||||||
|         factory="loops.compound.book.browser.BookView" |         factory="loops.compound.book.browser.BookView" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
|   <zope:adapter |   <zope:adapter | ||||||
|         name="section_view" |         name="section_view" | ||||||
|         for="loops.interfaces.IConcept |         for="loops.interfaces.IConcept | ||||||
|              loops.browser.skin.Lobo" |              zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|         provides="zope.interface.Interface" |         provides="zope.interface.Interface" | ||||||
|         factory="loops.compound.book.browser.SectionView" |         factory="loops.compound.book.browser.SectionView" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|   <zope:adapter |   <zope:adapter | ||||||
|         name="book_topic_view" |         name="book_topic_view" | ||||||
|         for="loops.interfaces.IConcept |         for="loops.interfaces.IConcept | ||||||
|              loops.browser.skin.Lobo" |              zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|         provides="zope.interface.Interface" |         provides="zope.interface.Interface" | ||||||
|         factory="loops.compound.book.browser.TopicView" |         factory="loops.compound.book.browser.TopicView" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| type(u'documenttype', u'Dokumentenart', options=u'qualifier:assign', | type(u'documenttype', u'Dokumentenart', options=u'qualifier:assign', | ||||||
|  |     typeInterface=u'loops.interfaces.IOptions', | ||||||
|     viewName=u'') |     viewName=u'') | ||||||
| 
 | 
 | ||||||
| # book types | # book types | ||||||
|  | @ -26,6 +27,7 @@ concept(u'quote', u'Zitat', u'documenttype') | ||||||
| concept(u'story', u'Geschichte', u'documenttype') | concept(u'story', u'Geschichte', u'documenttype') | ||||||
| concept(u'tip', u'Tipp', u'documenttype') | concept(u'tip', u'Tipp', u'documenttype') | ||||||
| concept(u'usecase', u'Fallbeispiel', u'documenttype') | concept(u'usecase', u'Fallbeispiel', u'documenttype') | ||||||
|  | concept(u'warning', u'Warnung', u'documenttype') | ||||||
| 
 | 
 | ||||||
| # book structure | # book structure | ||||||
| child(u'book', u'section', u'issubtype', usePredicate=u'ispartof') | child(u'book', u'section', u'issubtype', usePredicate=u'ispartof') | ||||||
|  |  | ||||||
|  | @ -40,11 +40,21 @@ | ||||||
|   <metal:info use-macro="view/concept_macros/concepttitle" /> |   <metal:info use-macro="view/concept_macros/concepttitle" /> | ||||||
|   <metal:info use-macro="item/book_macros/children" /> |   <metal:info use-macro="item/book_macros/children" /> | ||||||
|   <metal:text define-macro="textresources"> |   <metal:text define-macro="textresources"> | ||||||
|     <div tal:repeat="related item/textResources"> |     <div style="clear: both" | ||||||
|  |          tal:repeat="related item/textResources"> | ||||||
|       <div class="span-4"> |       <div class="span-4"> | ||||||
|         <div tal:attributes="class python: |         <div metal:define-macro="default_text" | ||||||
|                     item.getCssClassForResource(related)" |              tal:attributes="class python: | ||||||
|              tal:content="structure related/render" /> |                       item.getCssClassForResource(related)"> | ||||||
|  |           <h3 tal:define="ttitle python:item.getTitleForResource(related)" | ||||||
|  |               tal:condition="ttitle" | ||||||
|  |               tal:content="ttitle" /> | ||||||
|  |           <img class="flow-left" style="padding-top: 5px" | ||||||
|  |                tal:define="icon python:item.getIconForResource(related)" | ||||||
|  |                tal:condition="icon" | ||||||
|  |                tal:attributes="src icon" /> | ||||||
|  |           <span tal:content="structure related/render" /> | ||||||
|  |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="span-2 last" style="padding-top: 0.4em"> |       <div class="span-2 last" style="padding-top: 0.4em"> | ||||||
|         <div class="object-actions" style="padding-top: 0" |         <div class="object-actions" style="padding-top: 0" | ||||||
|  |  | ||||||
|  | @ -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 | #  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 | #  it under the terms of the GNU General Public License as published by | ||||||
|  | @ -78,28 +78,28 @@ class OfficeFile(ExternalFileAdapter): | ||||||
|     @Lazy |     @Lazy | ||||||
|     def docPropertyDom(self): |     def docPropertyDom(self): | ||||||
|         fn = self.docFilename |         fn = self.docFilename | ||||||
|         dummy = dict(core=[], custom=[]) |         result = dict(core=[], custom=[]) | ||||||
|         root, ext = os.path.splitext(fn) |         root, ext = os.path.splitext(fn) | ||||||
|         if not ext.lower() in self.fileExtensions: |         if not ext.lower() in self.fileExtensions: | ||||||
|             return dummy |             return result | ||||||
|         try: |         try: | ||||||
|             zf = ZipFile(fn, 'r') |             zf = ZipFile(fn, 'r') | ||||||
|         except IOError, e: |         except IOError, e: | ||||||
|             from logging import getLogger |             from logging import getLogger | ||||||
|             self.logger.warn(e) |             self.logger.warn(e) | ||||||
|             return dummy |             return result | ||||||
|         if self.corePropFileName not in zf.namelist(): |         if self.corePropFileName not in zf.namelist(): | ||||||
|             self.logger.warn('Core properties not found in file %s.' % |             self.logger.warn('Core properties not found in file %s.' % | ||||||
|                              self.externalAddress) |                              self.externalAddress) | ||||||
|  |         else: | ||||||
|  |             result['core'] = etree.fromstring(zf.read(self.corePropFileName)) | ||||||
|         if self.propFileName not in zf.namelist(): |         if self.propFileName not in zf.namelist(): | ||||||
|             self.logger.warn('Custom properties not found in file %s.' % |             self.logger.warn('Custom properties not found in file %s.' % | ||||||
|                              self.externalAddress) |                              self.externalAddress) | ||||||
|         propsXml = zf.read(self.propFileName) |         else: | ||||||
|         corePropsXml = zf.read(self.corePropFileName) |             result['custom'] = etree.fromstring(zf.read(self.propFileName)) | ||||||
|         # TODO: read core.xml, return both trees in dictionary |  | ||||||
|         zf.close() |         zf.close() | ||||||
|         return {'custom': etree.fromstring(propsXml), |         return result | ||||||
|                 'core': etree.fromstring(corePropsXml)} |  | ||||||
| 
 | 
 | ||||||
|     def getDocProperty(self, pname): |     def getDocProperty(self, pname): | ||||||
|         for p in self.docPropertyDom['custom']: |         for p in self.docPropertyDom['custom']: | ||||||
|  |  | ||||||
|  | @ -690,9 +690,21 @@ class IIndexAttributes(Interface): | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # reusable interface elements | ||||||
|  | 
 | ||||||
|  | class IOptions(Interface): | ||||||
|  | 
 | ||||||
|  |     options = schema.List( | ||||||
|  |         title=_(u'Options'), | ||||||
|  |         description=_(u'Additional settings.'), | ||||||
|  |         value_type=schema.TextLine(), | ||||||
|  |         default=[], | ||||||
|  |         required=False) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # types stuff | # types stuff | ||||||
| 
 | 
 | ||||||
| class ITypeConcept(IConceptSchema, ILoopsAdapter): | class ITypeConcept(IConceptSchema, ILoopsAdapter, IOptions): | ||||||
|     """ Concepts of type 'type' should be adaptable to this interface. |     """ Concepts of type 'type' should be adaptable to this interface. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  | @ -725,13 +737,6 @@ class ITypeConcept(IConceptSchema, ILoopsAdapter): | ||||||
|         default=u'', |         default=u'', | ||||||
|         required=False) |         required=False) | ||||||
| 
 | 
 | ||||||
|     options = schema.List( |  | ||||||
|         title=_(u'Options'), |  | ||||||
|         description=_(u'Additional settings.'), |  | ||||||
|         value_type=schema.TextLine(), |  | ||||||
|         default=[], |  | ||||||
|         required=False) |  | ||||||
| 
 |  | ||||||
|     # storage = schema.Choice() |     # storage = schema.Choice() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ class SurveyView(ConceptView): | ||||||
|                                mapping=dict(minAnswers=qugroup.minAnswers)), |                                mapping=dict(minAnswers=qugroup.minAnswers)), | ||||||
|                              target_language=lang) |                              target_language=lang) | ||||||
|         if info: |         if info: | ||||||
|             text = u'%s<br />(%s)' % (text, info) |             text = u'<i>%s</i><br />(%s)' % (text, info) | ||||||
|         return text |         return text | ||||||
| 
 | 
 | ||||||
|     def getValues(self, question): |     def getValues(self, question): | ||||||
|  |  | ||||||
|  | @ -38,6 +38,13 @@ class IQuestionnaire(IConceptSchema, interfaces.IQuestionnaire): | ||||||
|         default=4, |         default=4, | ||||||
|         required=True) |         required=True) | ||||||
| 
 | 
 | ||||||
|  |     feedbackHeader = schema.Text( | ||||||
|  |         title=_(u'Feedback Header'), | ||||||
|  |         description=_(u'Text that will appear at the top of the feedback page.'), | ||||||
|  |         default=u'', | ||||||
|  |         missing_value=u'', | ||||||
|  |         required=False) | ||||||
|  | 
 | ||||||
|     feedbackFooter = schema.Text( |     feedbackFooter = schema.Text( | ||||||
|         title=_(u'Feedback Footer'), |         title=_(u'Feedback Footer'), | ||||||
|         description=_(u'Text that will appear at the end of the feedback page.'), |         description=_(u'Text that will appear at the end of the feedback page.'), | ||||||
|  |  | ||||||
|  | @ -11,6 +11,9 @@ | ||||||
|   </tal:description> |   </tal:description> | ||||||
|   <div tal:condition="feedback"> |   <div tal:condition="feedback"> | ||||||
|     <h3 i18n:translate="">Feedback</h3> |     <h3 i18n:translate="">Feedback</h3> | ||||||
|  |     <div tal:define="header item/adapted/feedbackHeader" | ||||||
|  |          tal:condition="header" | ||||||
|  |          tal:content="structure python:item.renderText(header, 'text/restructured')" /> | ||||||
|     <table class="listing"> |     <table class="listing"> | ||||||
|       <tr> |       <tr> | ||||||
|         <th i18n:translate="">Category</th> |         <th i18n:translate="">Category</th> | ||||||
|  | @ -47,12 +50,12 @@ | ||||||
|       <table class="listing"> |       <table class="listing"> | ||||||
|         <tal:qugroup repeat="qugroup item/adapted/questionGroups"> |         <tal:qugroup repeat="qugroup item/adapted/questionGroups"> | ||||||
|           <tr><td colspan="6"> </td></tr> |           <tr><td colspan="6"> </td></tr> | ||||||
|           <tr> |           <tr class="vpad"> | ||||||
|             <td tal:define="infoText python:item.getInfoText(qugroup)"> |             <td tal:define="infoText python:item.getInfoText(qugroup)"> | ||||||
|               <b tal:content="qugroup/title" /> |               <b tal:content="qugroup/title" /> | ||||||
|               <div tal:condition="infoText"> |               <div class="infotext" | ||||||
|  |                    tal:condition="infoText"> | ||||||
|                 <span tal:content="structure infoText" /> |                 <span tal:content="structure infoText" /> | ||||||
|                 <br />  |  | ||||||
|               </div> |               </div> | ||||||
|             </td> |             </td> | ||||||
|             <td style="text-align: center" |             <td style="text-align: center" | ||||||
|  | @ -63,7 +66,8 @@ | ||||||
|                 style="text-align: right" |                 style="text-align: right" | ||||||
|                 i18n:translate="">Does not apply</td> |                 i18n:translate="">Does not apply</td> | ||||||
|           </tr> |           </tr> | ||||||
|           <tr tal:repeat="question qugroup/questions"> |           <tr class="vpad" | ||||||
|  |               tal:repeat="question qugroup/questions"> | ||||||
|             <td tal:content="question/text" /> |             <td tal:content="question/text" /> | ||||||
|             <td style="white-space: nowrap; text-align: center" |             <td style="white-space: nowrap; text-align: center" | ||||||
|                 tal:repeat="value python:item.getValues(question)"> |                 tal:repeat="value python:item.getValues(question)"> | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -3,7 +3,7 @@ msgstr "" | ||||||
| 
 | 
 | ||||||
| "Project-Id-Version: 0.13.0\n" | "Project-Id-Version: 0.13.0\n" | ||||||
| "POT-Creation-Date: 2007-05-22 12:00 CET\n" | "POT-Creation-Date: 2007-05-22 12:00 CET\n" | ||||||
| "PO-Revision-Date: 2013-04-06 12:00 CET\n" | "PO-Revision-Date: 2013-06-20 12:00 CET\n" | ||||||
| "Last-Translator: Helmut Merz <helmutm@cy55.de>\n" | "Last-Translator: Helmut Merz <helmutm@cy55.de>\n" | ||||||
| "Language-Team: loops developers <helmutm@cy55.de>\n" | "Language-Team: loops developers <helmutm@cy55.de>\n" | ||||||
| "MIME-Version: 1.0\n" | "MIME-Version: 1.0\n" | ||||||
|  | @ -86,6 +86,9 @@ msgstr "Thema bearbeiten..." | ||||||
| msgid "Modify topic." | msgid "Modify topic." | ||||||
| msgstr "Thema ändern" | msgstr "Thema ändern" | ||||||
| 
 | 
 | ||||||
|  | msgid "Please correct the indicated errors." | ||||||
|  | msgstr "Bitte berichtigen Sie die angezeigten Fehler." | ||||||
|  | 
 | ||||||
| # blog | # blog | ||||||
| 
 | 
 | ||||||
| msgid "Edit Blog Post..." | msgid "Edit Blog Post..." | ||||||
|  | @ -762,12 +765,27 @@ msgstr "Teilnehmerregistrierung" | ||||||
| msgid "Register" | msgid "Register" | ||||||
| msgstr "Benutzer registrieren" | msgstr "Benutzer registrieren" | ||||||
| 
 | 
 | ||||||
|  | msgid "Register new member" | ||||||
|  | msgstr "Neu registrieren" | ||||||
|  | 
 | ||||||
|  | msgid "Login name already taken." | ||||||
|  | msgstr "Die von Ihnen eingegebene Benutzerkennung ist schon vergeben." | ||||||
|  | 
 | ||||||
| msgid "Your old password was not entered correctly." | msgid "Your old password was not entered correctly." | ||||||
| msgstr "Sie haben Ihr altes Passwort nicht korrekt eingegeben." | msgstr "Sie haben Ihr altes Passwort nicht korrekt eingegeben." | ||||||
| 
 | 
 | ||||||
| msgid "Password and password confirmation do not match." | msgid "Password and password confirmation do not match." | ||||||
| msgstr "Die Passwort-Wiederholung stimmt nicht mit dem eingegebenen Passwort überein." | msgstr "Die Passwort-Wiederholung stimmt nicht mit dem eingegebenen Passwort überein." | ||||||
| 
 | 
 | ||||||
|  | msgid "confirmation_mail_subject" | ||||||
|  | msgstr "Benutzer-Registrierung" | ||||||
|  | 
 | ||||||
|  | msgid "confirmation_mail_text" | ||||||
|  | msgstr "Bitte clicken Sie auf den folgenden Link, um die Anmeldung abzuschließen." | ||||||
|  | 
 | ||||||
|  | msgid "The user account has been created." | ||||||
|  | msgstr "Ihr Benutzerkonto wurde eingerichtet." | ||||||
|  | 
 | ||||||
| msgid "Your password has been changed." | msgid "Your password has been changed." | ||||||
| msgstr "Ihr Passwort wurde geändert." | msgstr "Ihr Passwort wurde geändert." | ||||||
| 
 | 
 | ||||||
|  | @ -1010,14 +1028,26 @@ msgid "Restrict to objects with certain states" | ||||||
| msgstr "Auf Objekte mit bestimmtem Status beschränken" | msgstr "Auf Objekte mit bestimmtem Status beschränken" | ||||||
| 
 | 
 | ||||||
| msgid "Workflow" | msgid "Workflow" | ||||||
| msgstr "Statusdefinition/Workflow" | msgstr "Workflow" | ||||||
| 
 | 
 | ||||||
| msgid "States" | msgid "States" | ||||||
| msgstr "Statuswerte" | msgstr "Statuswerte" | ||||||
| 
 | 
 | ||||||
|  | msgid "States Definition" | ||||||
|  | msgstr "Workflowdefinition" | ||||||
|  | 
 | ||||||
|  | msgid "State Transition" | ||||||
|  | msgstr "Workflow-Statusänderung" | ||||||
|  | 
 | ||||||
|  | msgid "Transition" | ||||||
|  | msgstr "Aktion" | ||||||
|  | 
 | ||||||
| msgid "State information for $definition: $title" | msgid "State information for $definition: $title" | ||||||
| msgstr "Status ($definition): $title" | msgstr "Status ($definition): $title" | ||||||
| 
 | 
 | ||||||
|  | msgid "Available Transitions" | ||||||
|  | msgstr "Übergänge" | ||||||
|  | 
 | ||||||
| msgid "classification_quality" | msgid "classification_quality" | ||||||
| msgstr "Klassifizierung" | msgstr "Klassifizierung" | ||||||
| 
 | 
 | ||||||
|  | @ -1030,6 +1060,12 @@ msgstr "Aufgabe" | ||||||
| msgid "publishable_task" | msgid "publishable_task" | ||||||
| msgstr "Aufgabe/Zugriff" | msgstr "Aufgabe/Zugriff" | ||||||
| 
 | 
 | ||||||
|  | msgid "label_transition_comments" | ||||||
|  | msgstr "Bemerkung" | ||||||
|  | 
 | ||||||
|  | msgid "desc_transition_comments" | ||||||
|  | msgstr "Notizen zum Statusübergang." | ||||||
|  | 
 | ||||||
| # state names | # state names | ||||||
| 
 | 
 | ||||||
| msgid "accepted" | msgid "accepted" | ||||||
|  |  | ||||||
|  | @ -185,7 +185,7 @@ sure that a principal object can be served by a corresponding factory): | ||||||
|   ...         'lastName': u'Sawyer', |   ...         'lastName': u'Sawyer', | ||||||
|   ...         'firstName': u'Tom', |   ...         'firstName': u'Tom', | ||||||
|   ...         'email': u'tommy@sawyer.com', |   ...         'email': u'tommy@sawyer.com', | ||||||
|   ...         'action': 'update',} |   ...         'form.action': 'update',} | ||||||
| 
 | 
 | ||||||
| and register it. | and register it. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ Definition of view classes and other browser related stuff for | ||||||
| members (persons). | members (persons). | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | from datetime import datetime | ||||||
|  | from email.MIMEText import MIMEText | ||||||
| from zope import interface, component | from zope import interface, component | ||||||
| from zope.app.authentication.principalfolder import InternalPrincipal | from zope.app.authentication.principalfolder import InternalPrincipal | ||||||
| from zope.app.form.browser.textwidgets import PasswordWidget as BasePasswordWidget | from zope.app.form.browser.textwidgets import PasswordWidget as BasePasswordWidget | ||||||
|  | @ -29,6 +31,8 @@ from zope.app.principalannotation import annotations | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.i18nmessageid import MessageFactory | from zope.i18nmessageid import MessageFactory | ||||||
| from zope.security import checkPermission | from zope.security import checkPermission | ||||||
|  | from zope.sendmail.interfaces import IMailDelivery | ||||||
|  | from zope.traversing.browser import absoluteURL | ||||||
| 
 | 
 | ||||||
| from cybertools.composer.interfaces import IInstance | from cybertools.composer.interfaces import IInstance | ||||||
| from cybertools.composer.schema.browser.common import schema_macros | from cybertools.composer.schema.browser.common import schema_macros | ||||||
|  | @ -36,7 +40,8 @@ from cybertools.composer.schema.browser.form import Form, CreateForm | ||||||
| from cybertools.composer.schema.schema import FormState, FormError | from cybertools.composer.schema.schema import FormState, FormError | ||||||
| from cybertools.meta.interfaces import IOptions | from cybertools.meta.interfaces import IOptions | ||||||
| from cybertools.typology.interfaces import IType | from cybertools.typology.interfaces import IType | ||||||
| from loops.browser.common import concept_macros | from cybertools.util.randomname import generateName | ||||||
|  | from loops.browser.common import concept_macros, form_macros | ||||||
| from loops.browser.concept import ConceptView, ConceptRelationView | from loops.browser.concept import ConceptView, ConceptRelationView | ||||||
| from loops.browser.node import NodeView | from loops.browser.node import NodeView | ||||||
| from loops.common import adapted | from loops.common import adapted | ||||||
|  | @ -44,7 +49,7 @@ from loops.concept import Concept | ||||||
| from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager | from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager | ||||||
| from loops.organize.interfaces import IMemberRegistration, IPasswordChange | from loops.organize.interfaces import IMemberRegistration, IPasswordChange | ||||||
| from loops.organize.party import getPersonForUser, Person | from loops.organize.party import getPersonForUser, Person | ||||||
| from loops.organize.util import getInternalPrincipal | from loops.organize.util import getInternalPrincipal, getPrincipalForUserId | ||||||
| import loops.browser.util | import loops.browser.util | ||||||
| from loops.util import _ | from loops.util import _ | ||||||
| 
 | 
 | ||||||
|  | @ -74,10 +79,11 @@ class PersonalInfo(ConceptView): | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class MemberRegistration(NodeView, CreateForm): | class BaseMemberRegistration(NodeView): | ||||||
| 
 | 
 | ||||||
|     interface = IMemberRegistration     # TODO: add company, create institution |     interface = IMemberRegistration     # TODO: add company, create institution | ||||||
|     message = _(u'The user account has been created.') |     message = _(u'The user account has been created.') | ||||||
|  |     template = form_macros | ||||||
| 
 | 
 | ||||||
|     formErrors = dict( |     formErrors = dict( | ||||||
|         confirm_nomatch=FormError(_(u'Password and password confirmation do not match.')), |         confirm_nomatch=FormError(_(u'Password and password confirmation do not match.')), | ||||||
|  | @ -86,10 +92,23 @@ class MemberRegistration(NodeView, CreateForm): | ||||||
| 
 | 
 | ||||||
|     label = _(u'Member Registration') |     label = _(u'Member Registration') | ||||||
|     label_submit = _(u'Register') |     label_submit = _(u'Register') | ||||||
|  |     title = _('Member Registration') | ||||||
| 
 | 
 | ||||||
|     permissions_key = u'registration.permissions' |     permissions_key = u'registration.permissions' | ||||||
|     roles_key = u'registration.roles' |     roles_key = u'registration.roles' | ||||||
|     registration_adapter_key = u'registration.adapter' |     registration_adapter_key = u'registration.adapter' | ||||||
|  |     text_names_prefix = 'organize.member.registration' | ||||||
|  |     # texts: reg_info, reg_feedback, conf_mail, conf_info, conf_feedback | ||||||
|  |     info_key = 'reg_info' | ||||||
|  |     feedback_key = 'reg_feedback' | ||||||
|  | 
 | ||||||
|  |     isInnerHtml = False | ||||||
|  |     showAssignments = False | ||||||
|  |     form_action = 'register' | ||||||
|  |     versionInfo = None | ||||||
|  | 
 | ||||||
|  |     def closeAction(self, submit=True): | ||||||
|  |         return u'' | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def macro(self): |     def macro(self): | ||||||
|  | @ -106,6 +125,31 @@ class MemberRegistration(NodeView, CreateForm): | ||||||
|     def item(self): |     def item(self): | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def data(self): | ||||||
|  |         return self.request.form | ||||||
|  | 
 | ||||||
|  |     def getPrincipalAnnotation(self, principal): | ||||||
|  |         return annotations(principal).get(ANNOTATION_KEY, None) | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def infoText(self): | ||||||
|  |         name = '.'.join((self.text_names_prefix, self.info_key)) | ||||||
|  |         text = self.resourceManager.get(name) | ||||||
|  |         if text: | ||||||
|  |             return self.renderText(text.data) | ||||||
|  |         return u'' | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def feedbackUrl(self): | ||||||
|  |         name = '.'.join((self.text_names_prefix, self.feedback_key)) | ||||||
|  |         text = self.resourceManager.get(name) | ||||||
|  |         if text: | ||||||
|  |             return self.getUrlForTarget(text) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MemberRegistration(BaseMemberRegistration, CreateForm): | ||||||
|  | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def schema(self): |     def schema(self): | ||||||
|         schema = super(MemberRegistration, self).schema |         schema = super(MemberRegistration, self).schema | ||||||
|  | @ -113,13 +157,14 @@ class MemberRegistration(NodeView, CreateForm): | ||||||
|         schema.fields.reorder(-2, 'loginName') |         schema.fields.reorder(-2, 'loginName') | ||||||
|         return schema |         return schema | ||||||
|     # TODO: add company, create institution |     # TODO: add company, create institution | ||||||
|  | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def object(self): |     def object(self): | ||||||
|         return Person(Concept()) |         return Person(Concept()) | ||||||
| 
 | 
 | ||||||
|     def update(self): |     def update(self): | ||||||
|         form = self.request.form |         form = self.request.form | ||||||
|         if not form.get('action'): |         if not form.get('form.action'): | ||||||
|             return True |             return True | ||||||
|         instance = component.getAdapter(self.object, IInstance, name='editor') |         instance = component.getAdapter(self.object, IInstance, name='editor') | ||||||
|         instance.template = self.schema |         instance.template = self.schema | ||||||
|  | @ -155,30 +200,164 @@ class MemberRegistration(NodeView, CreateForm): | ||||||
|         return False |         return False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SecureMemberRegistration(MemberRegistration): | class SecureMemberRegistration(BaseMemberRegistration, CreateForm): | ||||||
| 
 | 
 | ||||||
|     permissions_key = u'secure_registration.permissions' |     permissions_key = u'secure_registration.permissions' | ||||||
|     roles_key = u'secure_registration.roles' |     roles_key = u'secure_registration.roles' | ||||||
|  |     email_key = 'reg_email' | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def schema(self): |     def schema(self): | ||||||
|         schema = super(MemberRegistration, self).schema |         schema = super(SecureMemberRegistration, self).schema | ||||||
|         schema.fields.remove('birthDate') |         schema.fields.remove('birthDate') | ||||||
|         schema.fields.remove('password') |         schema.fields.remove('password') | ||||||
|         schema.fields.remove('passwordConfirm') |         schema.fields.remove('passwordConfirm') | ||||||
|         schema.fields.remove('phoneNumbers') |         schema.fields.remove('phoneNumbers') | ||||||
|         schema.fields.reorder(-2, 'loginName') |         #schema.fields.reorder(-2, 'loginName') | ||||||
|         return schema |         return schema | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def macro(self): | ||||||
|  |         return organize_macros.macros['register'] | ||||||
| 
 | 
 | ||||||
| class ConfirmMemberRegistration(NodeView): |     @Lazy | ||||||
|  |     def object(self): | ||||||
|  |         return Person(Concept()) | ||||||
| 
 | 
 | ||||||
|     # TODO: control form via interface? |     def update(self): | ||||||
|  |         form = self.request.form | ||||||
|  |         if not form.get('form.action'): | ||||||
|  |             return True | ||||||
|  |         instance = component.getAdapter(self.object, IInstance, name='editor') | ||||||
|  |         instance.template = self.schema | ||||||
|  |         self.formState = formState = instance.applyTemplate(data=form, | ||||||
|  |                                             fieldHandlers=self.fieldHandlers) | ||||||
|  |         if formState.severity > 0: | ||||||
|  |             # show form again | ||||||
|  |             return True | ||||||
|  |         login = form.get('loginName') | ||||||
|  |         regMan = IMemberRegistrationManager(self.context.getLoopsRoot()) | ||||||
|  |         pw = generateName() | ||||||
|  |         email = form.get('email') | ||||||
|  |         try:     | ||||||
|  |             result = regMan.register(login, pw, | ||||||
|  |                                      form.get('lastName'), form.get('firstName'), | ||||||
|  |                                      email=email,) | ||||||
|  |         except ValueError, e: | ||||||
|  |             fi = formState.fieldInstances['loginName'] | ||||||
|  |             fi.setError('duplicate_loginname', self.formErrors) | ||||||
|  |             formState.severity = max(formState.severity, fi.severity) | ||||||
|  |             return True | ||||||
|  |         self.object = result | ||||||
|  |         person = result.context | ||||||
|  |         pa = self.getPrincipalAnnotation( | ||||||
|  |                     getPrincipalForUserId(adapted(person).getUserId())) | ||||||
|  |         pa['id'] = generateName() | ||||||
|  |         pa['timestamp'] = datetime.utcnow() | ||||||
|  |         self.notifyEmail(login, email, pa['id']) | ||||||
|  |         if self.feedbackUrl: | ||||||
|  |             self.request.response.redirect(self.feedbackUrl) | ||||||
|  |         else: | ||||||
|  |             msg = self.message | ||||||
|  |             self.request.response.redirect('%s?loops.message=%s' % (self.url, msg)) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     def notifyEmail(self, userid, recipient, id): | ||||||
|  |         baseUrl = absoluteURL(self.context.getMenu(), self.request) | ||||||
|  |         url = u'%s/selfservice_confirmation.html?login=%s&id=%s' % ( | ||||||
|  |                                     baseUrl, userid, id,) | ||||||
|  |         recipients = [recipient] | ||||||
|  |         subject = _(u'confirmation_mail_subject') | ||||||
|  |         name = '.'.join((self.text_names_prefix, self.email_key)) | ||||||
|  |         text = self.resourceManager.get(name) | ||||||
|  |         if text: | ||||||
|  |             message = (text.data % url).encode('UTF-8') | ||||||
|  |             subject = text.description or subject | ||||||
|  |         else: | ||||||
|  |             message = _(u'confirmation_mail_text') + u':\n\n' | ||||||
|  |             message = (message + url).encode('UTF-8') | ||||||
|  |         senderInfo = self.globalOptions('email.sender') | ||||||
|  |         sender = senderInfo and senderInfo[0] or 'info@loops.cy55.de' | ||||||
|  |         sender = sender.encode('UTF-8') | ||||||
|  |         msg = MIMEText(message, 'plain', 'utf-8') | ||||||
|  |         msg['Subject'] = subject.encode('UTF-8') | ||||||
|  |         msg['From'] = sender | ||||||
|  |         msg['To'] = ', '.join(recipients) | ||||||
|  |         mailhost = component.getUtility(IMailDelivery, 'Mail') | ||||||
|  |         mailhost.send(sender, recipients, msg.as_string()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ConfirmMemberRegistration(BaseMemberRegistration, Form): | ||||||
|  | 
 | ||||||
|  |     permissions_key = u'secure_registration.permissions' | ||||||
|  |     roles_key = u'secure_registration.roles' | ||||||
|  |     info_key = 'confirm_info' | ||||||
|  |     feedback_key = 'confirm_feedback' | ||||||
|  |     email_key = 'confirm_email' | ||||||
|  | 
 | ||||||
|  |     form_action = 'confirm_registration' | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def macro(self): |     def macro(self): | ||||||
|         return organize_macros.macros['confirm'] |         return organize_macros.macros['confirm'] | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def data(self): | ||||||
|  |         form = self.request.form | ||||||
|  |         return dict(loginName=form.get('login'), id=form.get('id')) | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def schema(self): | ||||||
|  |         schema = super(ConfirmMemberRegistration, self).schema | ||||||
|  |         schema.fields.remove('salutation') | ||||||
|  |         schema.fields.remove('academicTitle') | ||||||
|  |         schema.fields.remove('birthDate') | ||||||
|  |         schema.fields.remove('phoneNumbers') | ||||||
|  |         schema.fields.remove('loginName') | ||||||
|  |         schema.fields.remove('firstName') | ||||||
|  |         schema.fields.remove('lastName') | ||||||
|  |         schema.fields.remove('email') | ||||||
|  |         return schema | ||||||
|  | 
 | ||||||
|  |     def update(self): | ||||||
|  |         form = self.request.form | ||||||
|  |         if form.get('form.action') != 'confirm_registration': | ||||||
|  |             return True | ||||||
|  |         if not form.get('login'): | ||||||
|  |             return True | ||||||
|  |         regMan = IMemberRegistrationManager(self.context.getLoopsRoot()) | ||||||
|  |         prefix = regMan.getPrincipalFolderFromOption().prefix | ||||||
|  |         userId = prefix + form['login'] | ||||||
|  |         principal = getPrincipalForUserId(userId) | ||||||
|  |         pa = self.getPrincipalAnnotation(principal) | ||||||
|  |         id = form.get('id') | ||||||
|  |         if not id or id != pa.get('id'): | ||||||
|  |             return True | ||||||
|  |         instance = component.getAdapter(self.object, IInstance, name='editor') | ||||||
|  |         instance.template = self.schema | ||||||
|  |         self.formState = formState = instance.applyTemplate(data=form, | ||||||
|  |                                             fieldHandlers=self.fieldHandlers) | ||||||
|  |         #formState = self.formState = self.validate(form) | ||||||
|  |         if formState.severity > 0: | ||||||
|  |             return True | ||||||
|  |         pw = form.get('password') | ||||||
|  |         pwConfirm = form.get('passwordConfirm') | ||||||
|  |         if pw != pwConfirm: | ||||||
|  |             fi = formState.fieldInstances['password'] | ||||||
|  |             fi.setError('confirm_nomatch', self.formErrors) | ||||||
|  |             formState.severity = max(formState.severity, fi.severity) | ||||||
|  |             return True | ||||||
|  |         del pa['id'] | ||||||
|  |         del pa['timestamp'] | ||||||
|  |         ip = getInternalPrincipal(userId) | ||||||
|  |         ip.setPassword(pw) | ||||||
|  |         if self.feedbackUrl: | ||||||
|  |             self.request.response.redirect(self.feedbackUrl) | ||||||
|  |         else: | ||||||
|  |             url = '%s?loops.message=%s' % (self.url, self.message) | ||||||
|  |             self.request.response.redirect(url) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class PasswordChange(NodeView, Form): | class PasswordChange(NodeView, Form): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,40 @@ | ||||||
| <html i18n:domain="loops"> | <html i18n:domain="loops"> | ||||||
| 
 | 
 | ||||||
| <metal:registration define-macro="confirm"> | 
 | ||||||
| </metal:registration> | <metal:block define-macro="register"> | ||||||
|  |   <metal:data use-macro="view/form_macros/edit"> | ||||||
|  |     <metal:custom fill-slot="custom_header"> | ||||||
|  |       <tbody> | ||||||
|  |         <tr><td colspan="5"> | ||||||
|  |           <tal:info content="structure item/infoText" /> | ||||||
|  |         </td></tr> | ||||||
|  |       </tbody> | ||||||
|  |     </metal:custom> | ||||||
|  |   </metal:data> | ||||||
|  | </metal:block> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <metal:block define-macro="confirm"> | ||||||
|  |   <metal:data use-macro="view/form_macros/edit"> | ||||||
|  |     <metal:custom fill-slot="custom_header"> | ||||||
|  |       <tbody> | ||||||
|  |         <tr><td colspan="5"> | ||||||
|  |           <tal:info content="structure item/infoText" /> | ||||||
|  |         </td></tr> | ||||||
|  |         <tr><td colspan="5"> | ||||||
|  |           <input type="hidden" name="login" | ||||||
|  |                  tal:attributes="value item/data/loginName" /> | ||||||
|  |           <input type="hidden" name="id" | ||||||
|  |                  tal:attributes="value item/data/id" /> | ||||||
|  |           <table><tr> | ||||||
|  |             <td i18n:translate="">Login Name</td> | ||||||
|  |             <td tal:content="item/data/loginName" /> | ||||||
|  |           </tr></table> | ||||||
|  |         </td></tr> | ||||||
|  |       </tbody> | ||||||
|  |     </metal:custom> | ||||||
|  |   </metal:data> | ||||||
|  | </metal:block> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <metal:task define-macro="task"> | <metal:task define-macro="task"> | ||||||
|  |  | ||||||
|  | @ -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 | #  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 | #  it under the terms of the GNU General Public License as published by | ||||||
|  | @ -62,19 +62,29 @@ class MemberRegistrationManager(object): | ||||||
|     def __init__(self, context): |     def __init__(self, context): | ||||||
|         self.context = context |         self.context = context | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def personType(self): | ||||||
|  |         concepts = self.context.getConceptManager() | ||||||
|  |         return adapted(concepts[self.person_typeName]) | ||||||
|  | 
 | ||||||
|  |     def getPrincipalFolderFromOption(self): | ||||||
|  |         options = IOptions(self.personType) | ||||||
|  |         pfName = options(self.principalfolder_key, | ||||||
|  |                          (self.default_principalfolder,))[0] | ||||||
|  |         return getPrincipalFolder(self.context, pfName) | ||||||
|  | 
 | ||||||
|     def register(self, userId, password, lastName, firstName=u'', |     def register(self, userId, password, lastName, firstName=u'', | ||||||
|                  groups=[], useExisting=False, pfName=None, **kw): |                  groups=[], useExisting=False, pfName=None, **kw): | ||||||
|         concepts = self.context.getConceptManager() |         options = IOptions(self.personType) | ||||||
|         personType = adapted(concepts[self.person_typeName]) |  | ||||||
|         options = IOptions(personType) |  | ||||||
|         if pfName is None: |         if pfName is None: | ||||||
|             pfName = options(self.principalfolder_key, |             pfName = options(self.principalfolder_key, | ||||||
|                              (self.default_principalfolder,))[0] |                              (self.default_principalfolder,))[0] | ||||||
|         self.createPrincipal(pfName, userId, password, lastName, firstName, useExisting=useExisting) |         self.createPrincipal(pfName, userId, password, lastName, firstName,  | ||||||
|  |                              useExisting=useExisting) | ||||||
|         if not groups: |         if not groups: | ||||||
|             groups = options(self.groups_key, ()) |             groups = options(self.groups_key, ()) | ||||||
|         self.setGroupsForPrincipal(pfName, userId,  groups=groups) |         self.setGroupsForPrincipal(pfName, userId,  groups=groups) | ||||||
|         self.createPersonForPrincipal(pfName, userId, lastName, firstName, |         return self.createPersonForPrincipal(pfName, userId, lastName, firstName, | ||||||
|                                       useExisting, **kw) |                                       useExisting, **kw) | ||||||
| 
 | 
 | ||||||
|     def createPrincipal(self, pfName, userId, password, lastName, |     def createPrincipal(self, pfName, userId, password, lastName, | ||||||
|  |  | ||||||
|  | @ -26,9 +26,13 @@ from zope.cachedescriptors.property import Lazy | ||||||
| from zope.i18n import translate | from zope.i18n import translate | ||||||
| 
 | 
 | ||||||
| from cybertools.browser.action import Action, actions | from cybertools.browser.action import Action, actions | ||||||
|  | from cybertools.composer.schema.field import Field | ||||||
|  | from cybertools.composer.schema.interfaces import ISchemaFactory | ||||||
|  | from cybertools.composer.schema.schema import Schema | ||||||
| from cybertools.stateful.interfaces import IStateful, IStatesDefinition | from cybertools.stateful.interfaces import IStateful, IStatesDefinition | ||||||
| from loops.browser.common import BaseView | from loops.browser.common import BaseView | ||||||
| from loops.browser.concept import ConceptView | from loops.browser.concept import ConceptView | ||||||
|  | from loops.browser.form import ObjectForm, EditObject | ||||||
| from loops.expert.query import And, Or, State, Type, getObjects | from loops.expert.query import And, Or, State, Type, getObjects | ||||||
| from loops.expert.browser.search import search_template | from loops.expert.browser.search import search_template | ||||||
| from loops.security.common import checkPermission | from loops.security.common import checkPermission | ||||||
|  | @ -48,7 +52,7 @@ def registerStatesPortlet(controller, view, statesDefs, | ||||||
|     cm = controller.macros |     cm = controller.macros | ||||||
|     stfs = [component.getAdapter(view.context, IStateful, name=std)  |     stfs = [component.getAdapter(view.context, IStateful, name=std)  | ||||||
|                 for std in statesDefs] |                 for std in statesDefs] | ||||||
|     cm.register(region, 'states', title=_(u'States'), |     cm.register(region, 'states', title=_(u'Workflow'), | ||||||
|                 subMacro=template.macros['portlet_states'], |                 subMacro=template.macros['portlet_states'], | ||||||
|                 priority=priority, info=view, stfs=stfs) |                 priority=priority, info=view, stfs=stfs) | ||||||
| 
 | 
 | ||||||
|  | @ -79,8 +83,9 @@ class StateAction(Action): | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def icon(self): |     def icon(self): | ||||||
|         icon = self.stateObject.icon or 'led%s.png' % self.stateObject.color |         return self.stateObject.stateIcon | ||||||
|         return 'cybertools.icons/' + icon |         #icon = self.stateObject.icon or 'led%s.png' % self.stateObject.color | ||||||
|  |         #return 'cybertools.icons/' + icon | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def registerStatefulAction(std, msgFactory=_): | def registerStatefulAction(std, msgFactory=_): | ||||||
|  | @ -94,11 +99,67 @@ for std in statefulActions: | ||||||
|     registerStatefulAction(std) |     registerStatefulAction(std) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class ChangeStateBase(object): | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def stateful(self): | ||||||
|  |         return component.getAdapter(self.view.virtualTargetObject, IStateful, | ||||||
|  |                                     name=self.definition) | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def definition(self): | ||||||
|  |         return self.request.form.get('stdef') or u'' | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def action(self): | ||||||
|  |         return self.request.form.get('action') or u'' | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def transition(self): | ||||||
|  |         return self.stateful.getStatesDefinition().transitions[self.action] | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def stateObject(self): | ||||||
|  |         return self.stateful.getStateObject() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ChangeStateForm(ObjectForm, ChangeStateBase): | ||||||
|  | 
 | ||||||
|  |     form_action = 'change_state_action' | ||||||
|  |     data = {} | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def macro(self): | ||||||
|  |         return template.macros['change_state'] | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def title(self): | ||||||
|  |         return self.virtualTargetObject.title | ||||||
|  | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def schema(self): | ||||||
|  |         # TODO: create schema directly, use field information specified | ||||||
|  |         # in transition | ||||||
|  |         commentsField = Field('comments', _(u'label_transition_comments'),  | ||||||
|  |                               'textarea', | ||||||
|  |                               description=_(u'desc_transition_comments')) | ||||||
|  |         fields = [commentsField] | ||||||
|  |         return Schema(name='change_state', request=self.request,  | ||||||
|  |                       manager=self, *fields) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ChangeState(EditObject, ChangeStateBase): | ||||||
|  | 
 | ||||||
|  |     def update(self): | ||||||
|  |         print '***', self.request.form | ||||||
|  |         self.stateful.doTransition(self.action) | ||||||
|  |         return True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #class StateQuery(ConceptView): | #class StateQuery(ConceptView): | ||||||
| class StateQuery(BaseView): | class StateQuery(BaseView): | ||||||
| 
 | 
 | ||||||
|     template = template |     template = template | ||||||
| 
 |  | ||||||
|     form_action = 'execute_search_action' |     form_action = 'execute_search_action' | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|  |  | ||||||
|  | @ -77,7 +77,7 @@ | ||||||
|              set_schema="cybertools.stateful.interfaces.IStateful" /> |              set_schema="cybertools.stateful.interfaces.IStateful" /> | ||||||
|   </zope:class> |   </zope:class> | ||||||
| 
 | 
 | ||||||
|   <!-- views --> |   <!-- views and form controllers --> | ||||||
| 
 | 
 | ||||||
|   <browser:page |   <browser:page | ||||||
|         for="loops.interfaces.IConcept" |         for="loops.interfaces.IConcept" | ||||||
|  | @ -91,6 +91,19 @@ | ||||||
|         class="loops.organize.stateful.browser.FilterAllStates" |         class="loops.organize.stateful.browser.FilterAllStates" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
| 
 | 
 | ||||||
|  |   <browser:page | ||||||
|  |       name="change_state.html" | ||||||
|  |       for="loops.interfaces.INode" | ||||||
|  |       class="loops.organize.stateful.browser.ChangeStateForm" | ||||||
|  |       permission="zope.ManageContent" /> | ||||||
|  | 
 | ||||||
|  |   <zope:adapter | ||||||
|  |       name="change_state" | ||||||
|  |       for="loops.browser.node.NodeView | ||||||
|  |            zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|  |       factory="loops.organize.stateful.browser.ChangeState" | ||||||
|  |       permission="zope.ManageContent" /> | ||||||
|  | 
 | ||||||
|   <!-- event handlers --> |   <!-- event handlers --> | ||||||
| 
 | 
 | ||||||
|   <zope:subscriber handler="loops.organize.stateful.base.handleTransition" /> |   <zope:subscriber handler="loops.organize.stateful.base.handleTransition" /> | ||||||
|  |  | ||||||
|  | @ -59,6 +59,7 @@ def taskStates(): | ||||||
|         Transition('finish', 'finish', 'finished'), |         Transition('finish', 'finish', 'finished'), | ||||||
|         Transition('cancel', 'cancel', 'cancelled'), |         Transition('cancel', 'cancel', 'cancelled'), | ||||||
|         Transition('reopen', 're-open', 'draft'), |         Transition('reopen', 're-open', 'draft'), | ||||||
|  |         Transition('archive', 'archive', 'archived'), | ||||||
|         initialState='draft') |         initialState='draft') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -68,26 +68,31 @@ | ||||||
| </metal:query> | </metal:query> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <!-- portlets --> |  | ||||||
| 
 |  | ||||||
| <metal:actions define-macro="portlet_states"> | <metal:actions define-macro="portlet_states"> | ||||||
|     <div tal:repeat="stf macro/stfs"> |     <div tal:repeat="stf macro/stfs"> | ||||||
|       <div tal:condition="python:len(macro.stfs) > 1"> |       <div tal:condition="python:len(macro.stfs) > 1"> | ||||||
|         <span i18n:translate="">Workflow</span> |         <span i18n:translate="">States Definition</span> | ||||||
|         <span i18n:translate="" |         <span i18n:translate="" | ||||||
|               tal:content="stf/statesDefinition" /> |               tal:content="stf/statesDefinition" /> | ||||||
|       </div> |       </div> | ||||||
|       <div> |       <div tal:define="stateObject stf/getStateObject"> | ||||||
|         <b i18n:translate="">State</b>: |         <b i18n:translate="">State</b>:<br /> | ||||||
|  |         <span> | ||||||
|  |           <img style="margin-bottom: -1px" | ||||||
|  |                tal:attributes="src string:$resourceBase/${stateObject/stateIcon}" /> | ||||||
|           <span i18n:translate="" |           <span i18n:translate="" | ||||||
|               tal:content="stf/state" /> |                 tal:content="stateObject/title" /> | ||||||
|  |         </span> | ||||||
|       </div> |       </div> | ||||||
|       <div> |       <div><b i18n:translate="">Available Transitions</b>: | ||||||
|         <div><b i18n:translate="">Available Transitions</b>:</div> |  | ||||||
|         <ul> |         <ul> | ||||||
|           <li tal:repeat="action stf/getAvailableTransitionsForUser"> |           <li tal:repeat="action stf/getAvailableTransitionsForUser"> | ||||||
|             <a i18n:translate="" |             <a i18n:translate="" | ||||||
|                tal:attributes="href string:change_state.html" |                tal:define="baseUrl view/virtualTargetUrl; | ||||||
|  |                            url string:$baseUrl/change_state.html?action=${action/name}&stdef=${stf/statesDefinition}" | ||||||
|  |                tal:attributes="href url; | ||||||
|  |                                onClick string:objectDialog('change_state', '$url');; | ||||||
|  |                                               return false;" | ||||||
|                tal:content="action/title" /> |                tal:content="action/title" /> | ||||||
|           </li> |           </li> | ||||||
|         </ul> |         </ul> | ||||||
|  | @ -96,4 +101,49 @@ | ||||||
| </metal:actions> | </metal:actions> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | <metal:dialog define-macro="change_state"> | ||||||
|  |   <form name="stateful_changeState" method="post"> | ||||||
|  |     <div dojoType="dijit.layout.BorderContainer" | ||||||
|  |          style="width: 70em; height: 600px"> | ||||||
|  |       <div dojoType="dijit.layout.ContentPane" region="top"> | ||||||
|  |         <h1><span i18n:translate="">State Transition</span> - | ||||||
|  |           <span tal:content="view/title" /> | ||||||
|  |         </h1> | ||||||
|  |         <div> | ||||||
|  |           <span i18n:translate="">State</span>: | ||||||
|  |           <span i18n:translate="" | ||||||
|  |                 tal:content="view/stateful/state" /> - | ||||||
|  |           <span i18n:translate="">Transition</span>: | ||||||
|  |           <span i18n:translate="" | ||||||
|  |                 tal:content="view/transition/title" /> | ||||||
|  |         </div> | ||||||
|  |         <input type="hidden" name="form.action" value="change_state"> | ||||||
|  |         <input type="hidden" name="stdef" | ||||||
|  |                tal:attributes="value request/form/stdef|nothing"> | ||||||
|  |         <input type="hidden" name="action" | ||||||
|  |                tal:attributes="value request/form/action|nothing"> | ||||||
|  |       </div> | ||||||
|  |       <div dojoType="dijit.layout.ContentPane" region="center"> | ||||||
|  |         <table cellpadding="3" class="form"> | ||||||
|  |           <tbody><tr><td colspan="5" style="padding-right: 15px"> | ||||||
|  |                 <div id="form.fields"> | ||||||
|  |                   <metal:fields use-macro="view/fieldRenderers/fields" /> | ||||||
|  |                 </div> | ||||||
|  |           </td></tr></tbody> | ||||||
|  |         </table> | ||||||
|  |       </div> | ||||||
|  |       <div dojoType="dijit.layout.ContentPane" region="bottom"> | ||||||
|  |         <metal:buttons define-slot="buttons"> | ||||||
|  |           <input value="Save" type="submit"  | ||||||
|  |                  onClick="submit();; return false" | ||||||
|  |                  i18n:attributes="value"> | ||||||
|  |           <input type="button" value="Cancel" onClick="dialog.hide();" | ||||||
|  |                  i18n:attributes="value"> | ||||||
|  |         </metal:buttons> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </form> | ||||||
|  | </metal:dialog> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| </html> | </html> | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								query.py
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								query.py
									
										
									
									
									
								
							|  | @ -1,5 +1,5 @@ | ||||||
| # | # | ||||||
| #  Copyright (c) 2008 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 | #  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 | #  it under the terms of the GNU General Public License as published by | ||||||
|  | @ -18,8 +18,6 @@ | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| Query management stuff. | Query management stuff. | ||||||
| 
 |  | ||||||
| $Id$ |  | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from BTrees.IOBTree import IOBTree | from BTrees.IOBTree import IOBTree | ||||||
|  | @ -33,6 +31,7 @@ from zope.intid.interfaces import IIntIds | ||||||
| from cybertools.typology.interfaces import IType | from cybertools.typology.interfaces import IType | ||||||
| from loops.common import AdapterBase | from loops.common import AdapterBase | ||||||
| from loops.interfaces import IConcept, IConceptSchema, ILoopsAdapter | from loops.interfaces import IConcept, IConceptSchema, ILoopsAdapter | ||||||
|  | from loops.interfaces import IOptions | ||||||
| from loops.security.common import canListObject | from loops.security.common import canListObject | ||||||
| from loops.type import TypeInterfaceSourceList | from loops.type import TypeInterfaceSourceList | ||||||
| from loops.versioning.util import getVersion | from loops.versioning.util import getVersion | ||||||
|  | @ -182,7 +181,7 @@ class ConceptQuery(BaseQuery): | ||||||
| 
 | 
 | ||||||
| # QueryConcept: concept objects that allow querying the database. | # QueryConcept: concept objects that allow querying the database. | ||||||
| 
 | 
 | ||||||
| class IQueryConcept(IConceptSchema, ILoopsAdapter): | class IQueryConcept(IConceptSchema, ILoopsAdapter, IOptions): | ||||||
|     """ The schema for the query type. |     """ The schema for the query type. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  | @ -194,13 +193,6 @@ class IQueryConcept(IConceptSchema, ILoopsAdapter): | ||||||
|         default=u'', |         default=u'', | ||||||
|         required=False) |         required=False) | ||||||
| 
 | 
 | ||||||
|     options = schema.List( |  | ||||||
|         title=_(u'Options'), |  | ||||||
|         description=_(u'Additional settings.'), |  | ||||||
|         value_type=schema.TextLine(), |  | ||||||
|         default=[], |  | ||||||
|         required=False) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class QueryConcept(AdapterBase): | class QueryConcept(AdapterBase): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -231,6 +231,8 @@ class ConceptSecuritySetter(LoopsObjectSecuritySetter): | ||||||
|         setter.propagateSecurity(revert, updated) |         setter.propagateSecurity(revert, updated) | ||||||
| 
 | 
 | ||||||
|     def propagateSecurity(self, revert=False, updated=None): |     def propagateSecurity(self, revert=False, updated=None): | ||||||
|  |         if self.globalOptions('noPropagateSecurity'): | ||||||
|  |             return | ||||||
|         if updated is None: |         if updated is None: | ||||||
|             updated = set() |             updated = set() | ||||||
|         obj = self.baseObject |         obj = self.baseObject | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								type.py
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								type.py
									
										
									
									
									
								
							|  | @ -32,6 +32,7 @@ from cybertools.typology.type import BaseType, TypeManager | ||||||
| from cybertools.typology.interfaces import ITypeManager | from cybertools.typology.interfaces import ITypeManager | ||||||
| from loops.interfaces import ILoopsObject, IConcept, IResource | from loops.interfaces import ILoopsObject, IConcept, IResource | ||||||
| from loops.interfaces import ITypeConcept | from loops.interfaces import ITypeConcept | ||||||
|  | from loops.interfaces import IOptions | ||||||
| from loops.interfaces import IResourceAdapter, IFile, IExternalFile, IImage | from loops.interfaces import IResourceAdapter, IFile, IExternalFile, IImage | ||||||
| from loops.interfaces import ITextDocument, INote | from loops.interfaces import ITextDocument, INote | ||||||
| from loops.common import adapted | from loops.common import adapted | ||||||
|  | @ -276,7 +277,8 @@ class TypeInterfaceSourceList(object): | ||||||
| 
 | 
 | ||||||
|     implements(schema.interfaces.IIterableSource) |     implements(schema.interfaces.IIterableSource) | ||||||
| 
 | 
 | ||||||
|     typeInterfaces = (ITypeConcept, IFile, IExternalFile, ITextDocument, INote) |     typeInterfaces = (ITypeConcept, IFile, IExternalFile, ITextDocument, INote, | ||||||
|  |                       IOptions) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, context): |     def __init__(self, context): | ||||||
|         self.context = context |         self.context = context | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 hplattner
						hplattner