def test_protocol_document_is_locked_by_system_once_generated(self, browser): self.setup_generated_protocol(browser) browser.find('Protocol-My meeting').click() document = browser.context lockable = ILockable(document) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(SYS_LOCK)) self.assertFalse(lockable.can_safely_unlock(STEALABLE_LOCK)) lock_info = lockable.lock_info()[0] self.assertEqual(u'sys.lock', lock_info['type'].__name__)
def test_protocol_document_is_locked_by_system_once_generated( self, browser): self.setup_generated_protocol(browser) browser.find('Protocol-My meeting').click() document = browser.context lockable = ILockable(document) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(SYS_LOCK)) self.assertFalse(lockable.can_safely_unlock(STEALABLE_LOCK)) lock_info = lockable.lock_info()[0] self.assertEqual(u'sys.lock', lock_info['type'].__name__)
def test_submit_additional_document_creates_new_locked_document(self): committee = create(Builder('committee').titled('My committee')) document = create(Builder('document') .within(self.dossier) .titled(u'A Document') .with_dummy_content()) proposal = create(Builder('proposal') .within(self.dossier) .titled(u'My Proposal') .as_submitted() .having(committee=committee.load_model())) proposal.submit_additional_document(document) submitted_proposal = api.portal.get().restrictedTraverse( proposal.load_model().submitted_physical_path.encode('utf-8')) docs = submitted_proposal.listFolderContents() self.assertEqual(1, len(docs)) submitted_document = docs.pop() self.assertEqual(document.Title(), submitted_document.Title()) self.assertEqual(document.file.filename, submitted_document.file.filename) # submitted document should be locked by custom lock lockable = ILockable(submitted_document) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(MEETING_SUBMITTED_LOCK)) self.assertSubmittedDocumentCreated(proposal, document, submitted_document)
def is_locked_by_another_user(self): """Return False if the document is locked by the current user or is not locked at all, True otherwise. """ lockable = ILockable(self.context) return not lockable.can_safely_unlock()
def is_locked_by_another_user(self): """Return False if the document is locked by the current user or is not locked at all, True otherwise. """ lockable = ILockable(self.context) return not lockable.can_safely_unlock()
def test_decide_agenda_item_creates_locked_excerpt_in_dossier(self, browser): self.setup_excerpt_template() proposal = self.setup_proposal() # schedule view = 'unscheduled_proposals/{}/schedule'.format( proposal.load_model().proposal_id) browser.login().open(self.meeting_wrapper, view=view) agenda_item = AgendaItem.query.first() browser.login().open( self.meeting_wrapper, view='agenda_items/{}/decide'.format(agenda_item.agenda_item_id), data={'_authenticator': createToken()}) agenda_item = AgendaItem.query.first() # refresh proposal = agenda_item.proposal excerpt_in_dossier = proposal.excerpt_document.resolve_document() lockable = ILockable(excerpt_in_dossier) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(MEETING_EXCERPT_LOCK)) browser.open(excerpt_in_dossier) self.assertEqual(u'This document is a copy of the excerpt Fooo - ' u'C\xf6mmunity meeting from the meeting C\xf6mmunity ' u'meeting and cannot be edited directly.', info_messages()[0]) message_links = browser.css('.portalMessage.info a') self.assertEqual( 'http://nohost/plone/opengever-meeting-committeecontainer/committee-1/submitted-proposal-1/document-3', message_links[0].get('href')) self.assertEqual( 'http://nohost/plone/opengever-meeting-committeecontainer/committee-1/meeting-1/view', message_links[1].get('href'))
def test_decide_agenda_item_creates_locked_excerpt_in_dossier( self, browser): self.setup_excerpt_template() proposal = self.setup_proposal() # schedule view = 'unscheduled_proposals/{}/schedule'.format( proposal.load_model().proposal_id) browser.login().open(self.meeting_wrapper, view=view) agenda_item = AgendaItem.query.first() browser.login().open(self.meeting_wrapper, view='agenda_items/{}/decide'.format( agenda_item.agenda_item_id), data={'_authenticator': createToken()}) agenda_item = AgendaItem.query.first() # refresh proposal = agenda_item.proposal excerpt_in_dossier = proposal.excerpt_document.resolve_document() lockable = ILockable(excerpt_in_dossier) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(MEETING_EXCERPT_LOCK)) browser.open(excerpt_in_dossier) self.assertEqual( u'This document is a copy of the excerpt Fooo - ' u'C\xf6mmunity meeting from the meeting C\xf6mmunity ' u'meeting and cannot be edited directly.', info_messages()[0]) message_links = browser.css('.portalMessage.info a') self.assertEqual( 'http://nohost/plone/opengever-meeting-committeecontainer/committee-1/submitted-proposal-1/document-3', message_links[0].get('href')) self.assertEqual( 'http://nohost/plone/opengever-meeting-committeecontainer/committee-1/meeting-1/view', message_links[1].get('href'))
def test_submit_additional_document_creates_new_locked_document(self): committee = create(Builder('committee').titled('My committee')) document = create(Builder('document') .within(self.dossier) .titled(u'A Document') .with_dummy_content()) proposal = create(Builder('proposal') .within(self.dossier) .titled(u'My Proposal') .as_submitted() .having(committee=committee.load_model())) proposal.submit_additional_document(document) submitted_proposal = api.portal.get().restrictedTraverse( proposal.load_model().submitted_physical_path.encode('utf-8')) docs = submitted_proposal.listFolderContents() self.assertEqual(1, len(docs)) submitted_document = docs.pop() self.assertEqual(document.Title(), submitted_document.Title()) self.assertEqual(document.file.filename, submitted_document.file.filename) # submitted document should be locked by custom lock lockable = ILockable(submitted_document) self.assertTrue(lockable.locked()) self.assertTrue(lockable.can_safely_unlock(MEETING_SUBMITTED_LOCK)) self.assertSubmittedDocumentCreated(proposal, document, submitted_document)
def is_available_for_current_user(self): """Check whether the current meeting can be safely unlocked. This means the current meeting is not locked by another user. """ lockable = ILockable(self.context) return lockable.can_safely_unlock()
def safe_unlock(self, redirect=True): """Unlock the object if the current user has the lock """ lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock() if redirect: self.redirect()
def safe_unlock(self, redirect=True): """Unlock the object if the current user has the lock """ lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock() if redirect: self.redirect()
def is_locked_for_current_user(self): """True if this object is locked for the current user (i.e. the current user is not the lock owner) """ lockable = ILockable(aq_inner(self.context)) # Faster version - we rely on the fact that can_safely_unlock() is # True even if the object is not locked return not lockable.can_safely_unlock()
def is_locked_for_current_user(self): """True if this object is locked for the current user (i.e. the current user is not the lock owner) """ lockable = ILockable(aq_inner(self.context)) # Faster version - we rely on the fact that can_safely_unlock() is # True even if the object is not locked return not lockable.can_safely_unlock()
def is_available_for_current_user(self): """Check whether the current meeting can be safely unlocked. This means the current meeting is not locked by another user. """ lockable = ILockable(self.context) return lockable.can_safely_unlock()
def begin(self, formname, fieldname, structure='false'): """Begin inline editing - find the widget for the given field name in the given form (looked up as a view on the context), then hide the block with the id '${fieldname}-display' and display an edit form in its place. If 'structure' is 'true' (a string), then the inline editable field will eventually permit HTML input to be rendered unescaped. """ context = aq_inner(self.context) request = aq_inner(self.request) form = getMultiAdapter((context, request), name=formname) form = form.__of__(context) if fieldname.startswith(form.prefix): fieldname = fieldname[len(form.prefix)+1:] formlib_field = form.form_fields[fieldname] widgets = formlib.setUpEditWidgets((formlib_field,), form.prefix, context, request, ignore_request=True) widget = widgets[fieldname] display_id = '%s-display' % fieldname form_id = '%s-form' % fieldname ksscore = self.getCommandSet('core') zopecommands = self.getCommandSet('zope') plonecommands = self.getCommandSet('plone') # lock the context (or issue warning) locking = ILockable(context, None) if locking: if not locking.can_safely_unlock(): selector = ksscore.getHtmlIdSelector('plone-lock-status') zopecommands.refreshViewlet(selector, 'plone.abovecontent', 'plone.lockinfo') plonecommands.refreshContentMenu() return else: # we are locking the content locking.lock() plonecommands.issuePortalMessage('') # hide the existing display field display_selector = ksscore.getHtmlIdSelector(display_id) ksscore.addClass(display_selector, 'hiddenStructure') # show the form form_html = self.form_template(widget=widget, form_id=form_id, fieldname=fieldname, structure=structure) ksscore.insertHTMLAfter(display_selector, form_html)
def reply(self): lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock() if INonStealableLock.providedBy(self.context): noLongerProvides(self.context, INonStealableLock) # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) return lock_info(self.context)
def autoSetID(day, event): if not hasattr(day, 'date') or day.date is None: return title = day.date.strftime('%d.%m.%Y') normalizer = getUtility(IIDNormalizer) newId = normalizer.normalize(title) if title != day.title or newId != day.id: lockable = ILockable(day) if lockable.locked(): if not lockable.can_safely_unlock(): # can not modify locked object return lockable.unlock() day.title = title api.content.rename(obj=day, new_id=newId, safe_id=True) day.reindexObject()
def autoSetID(timeslot, event): if timeslot.startTime is None or timeslot.endTime is None: return title = timeslot.getTimeRange() normalizer = getUtility(IIDNormalizer) newId = normalizer.normalize(title) if title != timeslot.title or newId != timeslot.id: lockable = ILockable(timeslot) if lockable.locked(): if not lockable.can_safely_unlock(): # can not modify locked object return lockable.unlock() timeslot.title = title api.content.rename(obj=timeslot, new_id=newId, safe_id=True) timeslot.reindexObject()
def addTranslation(self, language, *args, **kwargs): """Adds a translation.""" canonical = self.getCanonical() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): parent = parent.getTranslation(language) or parent if self.hasTranslation(language): translation = self.getTranslation(language) raise AlreadyTranslated, translation.absolute_url() id = canonical.getId() while not parent.checkIdAvailable(id): id = '%s-%s' % (id, language) kwargs[config.KWARGS_TRANSLATION_KEY] = canonical if kwargs.get('language', None) != language: kwargs['language'] = language o = _createObjectByType(self.portal_type, parent, id, *args, **kwargs) # If there is a custom factory method that doesn't add the # translation relationship, make sure it is done now. if o.getCanonical() != canonical: o.addTranslationReference(canonical) self.invalidateTranslationCache() # Copy over the language independent fields schema = canonical.Schema() independent_fields = schema.filterFields(languageIndependent=True) for field in independent_fields: accessor = field.getEditAccessor(canonical) if not accessor: accessor = field.getAccessor(canonical) data = accessor() translation_mutator = getattr(o, field.translation_mutator) translation_mutator(data) # If this is a folder, move translated subobjects aswell. if self.isPrincipiaFolderish: moveids = [] for obj in self.objectValues(): if ITranslatable.providedBy(obj) and \ obj.getLanguage() == language: lockable = ILockable(obj, None) if lockable is not None and lockable.can_safely_unlock(): lockable.unlock() moveids.append(obj.getId()) if moveids: o.manage_pasteObjects(self.manage_cutObjects(moveids)) o.reindexObject() if isDefaultPage(canonical, self.REQUEST): o._lp_default_page = True
def xmChangeWorkflowState(self, uid, url): """Change the workflow state, currently only of a Task.""" context = aq_inner(self.context) ksscore = self.getCommandSet('core') zopecommands = self.getCommandSet('zope') plonecommands = self.getCommandSet('plone') locking = ILockable(context, None) if locking is not None and not locking.can_safely_unlock(): selector = ksscore.getHtmlIdSelector('plone-lock-status') zopecommands.refreshViewlet(selector, 'plone.abovecontent', 'plone.lockinfo') plonecommands.refreshContentMenu() return self.render() (proto, host, path, query, anchor) = urlsplit(url) if not path.endswith('content_status_modify'): raise KSSExplicitError('only content_status_modify is handled') action = query.split("workflow_action=")[-1].split('&')[0] uid_catalog = getToolByName(context, 'uid_catalog') brain = uid_catalog(UID=uid)[0] obj = brain.getObject() # This may give a UnicodeDecodeError if the title has # non-ascii characters: # obj.content_status_modify(action) # So we do it manually, which is better anyway: wftool = getToolByName(context, 'portal_workflow') wftool.doActionFor(obj, action=action) if IXMStory.providedBy(self.context): # Only refresh content if the context is a Story, # otherwise you get too much tasks listed. selector = ksscore.getCssSelector('.contentViews') zopecommands.refreshViewlet(selector, 'plone.contentviews', 'plone.contentviews') zopecommands.refreshProvider('.tasklist_table', 'xm.tasklist.simple') plonecommands.refreshContentMenu() else: # In all other cases, we can at least refresh the part # that shows the workflow info for this item. wf_change = obj.restrictedTraverse('xm_workflow_change') html = wf_change() selector = ksscore.getHtmlIdSelector('id-%s' % uid) ksscore.replaceHTML(selector, html) self.issueAllPortalMessages() self.cancelRedirect()
def xmChangeWorkflowState(self, uid, url): """Change the workflow state, currently only of a Task.""" context = aq_inner(self.context) ksscore = self.getCommandSet('core') zopecommands = self.getCommandSet('zope') plonecommands = self.getCommandSet('plone') locking = ILockable(context, None) if locking is not None and not locking.can_safely_unlock(): selector = ksscore.getHtmlIdSelector('plone-lock-status') zopecommands.refreshViewlet(selector, 'plone.abovecontent', 'plone.lockinfo') plonecommands.refreshContentMenu() return self.render() (proto, host, path, query, anchor) = urlsplit(url) if not path.endswith('content_status_modify'): raise KSSExplicitError('only content_status_modify is handled') action = query.split("workflow_action=")[-1].split('&')[0] uid_catalog = getToolByName(context, 'uid_catalog') brain = uid_catalog(UID=uid)[0] obj = brain.getObject() # This may give a UnicodeDecodeError if the title has # non-ascii characters: # obj.content_status_modify(action) # So we do it manually, which is better anyway: wftool = getToolByName(context, 'portal_workflow') wftool.doActionFor(obj, action=action) if IXMStory.providedBy(self.context): # Only refresh content if the context is a Story, # otherwise you get too much tasks listed. selector = ksscore.getCssSelector('.contentViews') zopecommands.refreshViewlet(selector, 'plone.contentviews', 'plone.contentviews') zopecommands.refreshProvider('.tasklist_table', 'xm.tasklist.simple') plonecommands.refreshContentMenu() else: # In all other cases, we can at least refresh the part # that shows the workflow info for this item. wf_change = obj.restrictedTraverse('xm_workflow_change') html = wf_change() selector = ksscore.getHtmlIdSelector('id-%s' % uid) ksscore.replaceHTML(selector, html) self.issueAllPortalMessages() self.cancelRedirect()
def test_submit_additional_document(self): self.login(self.administrator) ori_document = self.subdocument with self.observe_children(self.submitted_proposal) as children: with self.login(self.dossier_responsible): self.proposal.submit_additional_document(ori_document) self.assertEquals(1, len(children['added'])) submitted_document, = children['added'] self.assertEqual(ori_document.Title(), submitted_document.Title()) self.assertEqual(ori_document.file.filename, submitted_document.file.filename) self.assertSubmittedDocumentCreated(self.proposal, ori_document) # submitted document should be locked by custom lock lockable = ILockable(submitted_document) self.assertTrue(lockable.locked()) self.assertFalse(lockable.can_safely_unlock(MEETING_SUBMITTED_LOCK))
def replaceWithView(self, fieldname, templateId, macro, uid=None, target=None, edit=False): """ kss commands to replace the edit widget by the field view """ ksscore = self.getCommandSet('core') instance = self._getFieldContext(uid) locking = ILockable(instance, None) if locking and locking.can_safely_unlock(): locking.unlock() html = self.renderViewField(fieldname, templateId, macro, uid) html = html.strip() field_id = target or "parent-fieldname-%s" % fieldname ksscore.replaceHTML(ksscore.getHtmlIdSelector(field_id), html) return self.render()
def reply(self): lockable = ILockable(self.context, None) if lockable is None: return lock_info(self.context) data = json_body(self.request) # Remove lock by the same user or steal it if lockable.can_safely_unlock() or data.get("force"): lockable.unlock() if INonStealableLock.providedBy(self.context): noLongerProvides(self.context, INonStealableLock) # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) return lock_info(self.context)
def autoSetID(person, event): # only managers are allowed to create / modify persons via forms if not api.user.has_permission('cmf.ModifyPortalContent', obj=person): return if person.email is None \ or person.prename is None \ or person.surname is None: return title = u'{0} {1}'.format(person.prename, person.surname) newId = emailToPersonId(person.email) if title != person.title or newId != person.id: lockable = ILockable(person) if lockable.locked(): if not lockable.can_safely_unlock(): # can not modify locked object return lockable.unlock() person.title = title api.content.rename(obj=person, new_id=newId, safe_id=True) person.reindexObject()
def cancel(self, fieldname): """Cancel the inline editing taking place for the given field, by removing the inline editing form and unhiding the block with id '${fieldname}-display'. """ context = aq_inner(self.context) display_id = '%s-display' % fieldname form_id = '%s-form' % fieldname ksscore = self.getCommandSet('core') # unlock the context if it was locked before locking = ILockable(context, None) if locking and locking.can_safely_unlock(): locking.unlock() # show the existing display field ksscore.removeClass(ksscore.getHtmlIdSelector(display_id), 'hiddenStructure') # hide the form ksscore.deleteNode(ksscore.getHtmlIdSelector(form_id))
def changeWorkflowState(self, url): context = aq_inner(self.context) ksscore = self.getCommandSet('core') zopecommands = self.getCommandSet('zope') plonecommands = self.getCommandSet('plone') locking = ILockable(context, None) if locking is not None and not locking.can_safely_unlock(): selector = ksscore.getHtmlIdSelector('plone-lock-status') zopecommands.refreshViewlet(selector, 'plone.abovecontent', 'plone.lockinfo') plonecommands.refreshContentMenu() return self.render() (proto, host, path, query, anchor) = urlsplit(url) if not path.endswith('content_status_modify'): raise KSSExplicitError, 'content_status_modify is not handled' action = query.split("workflow_action=")[-1].split('&')[0] context.content_status_modify(action) selector = ksscore.getCssSelector('.contentViews') zopecommands.refreshViewlet(selector, 'plone.contentviews', 'plone.contentviews') plonecommands.refreshContentMenu() self.issueAllPortalMessages() self.cancelRedirect()
def createTranslation(self, container, language, *args, **kwargs): context = aq_inner(self.context) canonical = context.getCanonical() portal_type = self.getTranslationPortalType(container, language) new_id = kwargs.pop( 'id', self.generateId(container, canonical.getId(), language)) kwargs["language"] = language translation = _createObjectByType(portal_type, container, new_id, *args, **kwargs) # If there is a custom factory method that doesn't add the # translation relationship, make sure it is done now. if translation.getCanonical() != canonical: translation.addTranslationReference(canonical) # THIS IS THE LINE WE NEED TO CUSTOMIZE OSHALanguageIndependentFields(canonical).copyFields(translation) if isDefaultPage(aq_parent(aq_inner(canonical)), canonical): translation._lp_default_page = True # If this is a folder, move translated subobjects aswell. if context.isPrincipiaFolderish: moveids = [] for obj in context.values(): translator = ITranslatable(obj, None) if translator is not None \ and translator.getLanguage() == language: lockable = ILockable(obj, None) if lockable is not None and lockable.can_safely_unlock(): lockable.unlock() moveids.append(obj.getId()) if moveids: info = context.manage_cutObjects(moveids) translation.manage_pasteObjects(info) return translation
def createTranslation(self, container, language, *args, **kwargs): context = aq_inner(self.context) canonical = context.getCanonical() portal_type = self.getTranslationPortalType(container, language) new_id = kwargs.pop( 'id', self.generateId(container, canonical.getId(), language)) kwargs["language"] = language translation = _createObjectByType(portal_type, container, new_id, *args, **kwargs) # If there is a custom factory method that doesn't add the # translation relationship, make sure it is done now. if translation.getCanonical() != canonical: translation.addTranslationReference(canonical) # THIS IS THE LINE WE NEED TO CUSTOMIZE OSHALanguageIndependentFields(canonical).copyFields(translation) if isDefaultPage(aq_parent(aq_inner(canonical)), canonical): translation._lp_default_page = True # If this is a folder, move translated subobjects aswell. if context.isPrincipiaFolderish: moveids = [] for obj in context.values(): translator = ITranslatable(obj, None) if translator is not None \ and translator.getLanguage() == language: lockable = ILockable(obj, None) if lockable is not None and lockable.can_safely_unlock(): lockable.unlock() moveids.append(obj.getId()) if moveids: info = context.manage_cutObjects(moveids) translation.manage_pasteObjects(info) return translation
def replaceField(self, fieldname, templateId, macro, uid=None, target=None, edit=False): """ kss commands to replace the field value by the edit widget The edit parameter may be used if we are coming from something else than an edit view. """ ksscore = self.getCommandSet('core') zopecommands = self.getCommandSet('zope') plonecommands = self.getCommandSet('plone') instance = self._getFieldContext(uid) if edit: locking = ILockable(instance, None) if locking: if not locking.can_safely_unlock(): selector = ksscore.getHtmlIdSelector('plone-lock-status') zopecommands.refreshViewlet(selector, 'plone.abovecontent', 'plone.lockinfo') plonecommands.refreshContentMenu() return self.render() else: # were are locking the content locking.lock() plonecommands.issuePortalMessage('') html = self.renderEditField(fieldname, templateId, macro, uid) html = html.strip() field_id = target or "parent-fieldname-%s" % fieldname ksscore.replaceHTML(ksscore.getHtmlIdSelector(field_id), html) ksscore.focus("#%s .firstToFocus" % field_id) return self.render()
def unlock(self): lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock()
def unlock(self): lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock()
def unlockAfterModification(obj, event): """Release the DAV lock after save """ lockable = ILockable(obj) if lockable.can_safely_unlock(): lockable.unlock()
def safe_unlock(self): """Unlock the object if the current user has the lock """ lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock()
def unlockAfterModification(obj, event): """Release the DAV lock after save """ lockable = ILockable(obj) if lockable.can_safely_unlock(): lockable.unlock()
def test_can_safely_unlock_is_true_if_no_lock_exists(self): lockable = ILockable(self.wrapper) self.assertTrue(lockable.can_safely_unlock())
def safe_unlock(self): """Unlock the object if the current user has the lock """ lockable = ILockable(self.context) if lockable.can_safely_unlock(): lockable.unlock()