class ExampleWorkflowConstraintDefinition(constraints.ConstraintDefinition): @message(_('The parent object needs to be published first.')) @error_on(interfaces.PUBLISH) @warning_on(interfaces.SUBMIT) def parent_needs_to_be_published(self): return self.state().is_parent_published() @message(_('The child object ${item} is still published.')) @warning_on(interfaces.RETRACT) def retract_children_first(self): return list(self.state().get_published_children()) @message(_('The referenced object ${item} is not yet published.')) @warning_on(interfaces.PUBLISH, interfaces.SUBMIT) def references_should_be_published(self): main_obj = get_main_obj_belonging_to(self.context) def reference_is_not_reference_to_self(target_main_obj): return target_main_obj != main_obj return list(filter(reference_is_not_reference_to_self, map(get_main_obj_belonging_to, self.state().get_unpublished_references()))) @message(_('The referenced object ${item} is still published.')) @warning_on(interfaces.DELETE, interfaces.RETRACT) def references_may_be_retracted_too(self): return list(map(get_main_obj_belonging_to, self.state().get_published_references()))
class IRealm(Interface): active = Bool(title=_(u'label_realm_active', default=u'Active')) url = URI(title=_(u'label_realm_url', u'URL to the Plone-Site')) username = TextLine(title=_(u'label_realm_username', u'Username')) password = Password(title=_(u'label_realm_password', u'Password'))
class ConstraintDefinition1(constraints.ConstraintDefinition): @constraints.message(_('Fohoo')) @constraints.error_on(interfaces.PUBLISH) @constraints.warning_on(interfaces.SUBMIT) def foo(self): return False @constraints.message(_('Bahar')) @constraints.error_on(interfaces.PUBLISH, interfaces.SUBMIT) def bar(self): return False
def __call__(self, *args, **kwargs): redirect_to = None self.key = int(self.request.get('job')) self.job = self.queue.get_executed_job_by_key(self.key) if self.request.get('button.requeue'): if self.job.json_file_exists(): self.queue.remove_executed_job(self.key) self.job.move_jsonfile_to(self.config.getDataFolder()) self.queue.appendJob(self.job) msg = _(u'info_requeued_job', default=u'The job has been moved to the queue.') IStatusMessage(self.request).addStatusMessage(msg, type='info') redirect_to = './@@publisher-config' else: msg = _(u'error_job_data_file_missing', default=u'The data file of the job is missing.') IStatusMessage(self.request).addStatusMessage(msg, type='error') if self.request.get('button.delete'): self.queue.remove_executed_job(self.key) if self.job.json_file_exists(): self.job.removeJob() msg = _(u'info_job_deleted', default=u'The job has been deleted.') IStatusMessage(self.request).addStatusMessage(msg, type='info') redirect_to = './@@publisher-config-listExecutedJobs' if self.request.get('button.execute'): if self.job.json_file_exists(): portal = self.context.portal_url.getPortalObject() execview = portal.restrictedTraverse( '@@publisher.executeQueue') execview.execute_single_job(self.job) msg = _(u'info_job_executed', default=u'The job has been executed.') IStatusMessage(self.request).addStatusMessage(msg, type='info') else: msg = _(u'error_job_data_file_missing', default=u'The data file of the job is missing.') IStatusMessage(self.request).addStatusMessage(msg, type='error') redirect_to = './@@publisher-config-executed-job-details?job=' + \ str(self.key) if redirect_to: return self.request.RESPONSE.redirect(redirect_to) return super(ExecutedJobDetails, self).__call__(*args, **kwargs)
def __call__(self, no_response=False, msg=None, *args, **kwargs): """ This is a copy from the orginal publisher.publish view for PloneFormGen. it also publishs all its contents """ self.logger = getLogger() # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job queue = Queue(self.context) queue.createJob("push", self.context, username) self.logger.info( 'Created "%s" Job for "%s" at %s' % ("push", self.context.Title(), "/".join(self.context.getPhysicalPath())) ) # Get all items recursively and add theme to the queue def add_item_to_queue(items): for item in items: queue.createJob("push", item, username) subitems = item.objectValues() if subitems: add_item_to_queue(subitems) add_item_to_queue(self.context.objectValues()) # status message if msg is None: msg = _(u"This object has been added to the queue.") IStatusMessage(self.request).addStatusMessage(msg, type="info") if not no_response: return self.request.RESPONSE.redirect("./view")
def get_translated_cleanup_prompt(self): """Returns the converted prompt string which is displayed when clicking on "cleanup". """ return self.context.translate(_( u'prompt_cleanup', default=u'Are you shure to delete all executed jobs?'))
class CreateRealmForm(form.Form): """ The CreateRealmForm is a z3c-form used for adding a new Realm instance to the publisher configuration. @cvar fields: fields from the schema IRealmSchema @cvar ignoreContext: do not use context (z3c-form setting) @cvar label: label of the form """ implements(IWrappedForm) fields = field.Fields(IRealmSchema) ignoreContext = True label = u'Add Realm' @button.buttonAndHandler(_(u'button_save_realm', default=u'Save Realm')) def handleAdd(self, action): """ This handler handles a click on the "Add Realm"-Button. If no errors occured, it adds a new Realm to the Config. @param action: ActionInfo object provided by z3c.form @return: None (form is shown) or Response-redirect """ data, errors = self.extractData() config = IConfig(self.context) if len(errors)==0: assert config.is_update_realms_possible() # url + username has to be unique for realm in config.getRealms(): if realm.url==data['url'] and realm.username==data['username']: self.statusMessage( 'This URL / Username combination already exists!', 'error') return kwargs = { 'active': data['active'] and 1 or 0, 'url': data['url'], 'username': data['username'], 'password': data['password'], } realm = Realm(**kwargs) config.appendRealm(realm) self.statusMessage('Added realm successfully') return self.request.RESPONSE.redirect('./@@publisher-config') def statusMessage(self, message, type='info'): """ Adds a Plone statusMessage to the session. @param message: Message to display @type message: string @param type: Type of the message [info|warning|error] @type type: string @return: None """ IStatusMessage(self.request).addStatusMessage( message, type=type)
def __call__(self, event, no_response=False, msg=None): if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning( 'Could not create move job for blacklisted object (%s at %s)' % (self.context.Title(), '/'.join( self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False # This View should not be executed at the PloneSiteRoot if IPloneSiteRoot.providedBy(self.context): raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) additional_data = { 'move_data': { 'newName': event.newName, 'newParent': get_site_relative_path(event.newParent), 'newTitle': event.object.Title().decode('utf-8'), 'oldName': event.oldName, 'oldParent': get_site_relative_path(event.oldParent), } } queue.createJob('move', self.context, username, additional_data=additional_data) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'move', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) # status message if msg is None: msg = _(u'Object move/rename action has been added to the queue.') IStatusMessage(self.request).addStatusMessage(msg, type='info') if not no_response: return self.request.RESPONSE.redirect('./view')
class ConstraintDefinition2(constraints.ConstraintDefinition): @constraints.message(_('X ${item} Y')) @constraints.error_on(interfaces.PUBLISH) def baz(self): foo = Dummy(absolute_url=lambda: 'http://host/foo', Title=lambda: 'Foo') bar = Dummy(absolute_url=lambda: 'http://host/bar', Title=lambda: 'Bar') return [foo, bar]
def action(self, REQUEST=None): if self.request.get('button.requeue'): if self.job.json_file_exists(): self.queue.remove_executed_job(self.key) self.job.move_jsonfile_to(self.config.getDataFolder()) self.queue.appendJob(self.job) msg = _(u'info_requeued_job', default=u'The job has been moved to the queue.') IStatusMessage(self.request).addStatusMessage(msg, type='info') return './@@publisher-config' else: msg = _(u'error_job_data_file_missing', default=u'The data file of the job is missing.') IStatusMessage(self.request).addStatusMessage(msg, type='error') if self.request.get('button.delete'): self.queue.remove_executed_job(self.key) if self.job.json_file_exists(): self.job.removeJob() msg = _(u'info_job_deleted', default=u'The job has been deleted.') IStatusMessage(self.request).addStatusMessage(msg, type='info') return './@@publisher-config-listExecutedJobs' if self.request.get('button.execute'): if self.job.json_file_exists(): portal = self.context.portal_url.getPortalObject() execview = portal.restrictedTraverse( '@@publisher.executeQueue') execview.execute_single_job(self.job) msg = _(u'info_job_executed', default=u'The job has been executed.') IStatusMessage(self.request).addStatusMessage(msg, type='info') else: msg = _(u'error_job_data_file_missing', default=u'The data file of the job is missing.') IStatusMessage(self.request).addStatusMessage(msg, type='error') return './@@publisher-config-executed-job-details?job=' + \ str(self.key)
def __call__(self, *args, **kwargs): id = self.request.get('id', '') realm = self.getRealmById(id) if not realm: self.statusMessage(_(u'error_realm_not_found', default=u'Could not find realm'), 'error') else: responseText = sendRequestToRealm({}, realm, 'publisher.testConnection') if responseText=='ok': self.statusMessage(_(u'info_realm_connection_okay', default=u'Connection okay')) else: self.statusMessage( _(u'error_realm_connection_failed', default=u'Connection to realm failed: ${msg}', mapping=dict(msg=responseText.decode('utf-8'))), type='error') return self.request.RESPONSE.redirect('./@@publisher-config')
def __call__(self, *args, **kwargs): delete = self.request.get('delete', None) if delete: if self.config.removePathFromBlacklist(delete): msg = _(u'info_path_removed', default=u'Removed path ${path} from blacklist', mapping={'path': delete}) IStatusMessage(self.request).addStatusMessage(msg, type='info') return self.request.RESPONSE.redirect('./@@publisher-config-blacklist') return super(PathBlacklistView, self).__call__(*args, **kwargs)
def __call__(self, no_response=False, msg=None, *args, **kwargs): """ The __call__ method is used to execute the BrowserView. It creates and adds a "PUSH"-Job on the current context to the queue. @param args: list of unnamed arguments @type args: list @param kwargs: dict of named keyword-arguments @type kwargs: dict @return: Redirect to object`s default view """ if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning('Could not create push job for blacklisted '+\ 'object (%s at %s)' % ( self.context.Title(), '/'.join(self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False # mle: now its possible to execite this view on plonesiteroot # This View should not be executed at the PloneSiteRoot #if IPloneSiteRoot.providedBy(self.context): # raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) queue.createJob('push', self.context, username) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'push', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) # status message if msg is None: msg = _(u'This object has been added to the queue.') IStatusMessage(self.request).addStatusMessage( msg, type='info' ) if not no_response: return self.request.RESPONSE.redirect('./view')
def __call__(self, *args, **kwargs): delete = self.request.get('delete', None) if delete: if self.config.removePathFromBlacklist(delete): msg = _(u'info_path_removed', default=u'Removed path ${path} from blacklist', mapping={'path': delete}) IStatusMessage(self.request).addStatusMessage(msg, type='info') return self.request.RESPONSE.redirect( './@@publisher-config-blacklist') return super(PathBlacklistView, self).__call__(*args, **kwargs)
class AddPathForm(form.Form): implements(IWrappedForm) fields = field.Fields(IBlacklistPathSchema) ignoreContext = True label = _(u'form_label_add_path', default=u'Add path') @button.buttonAndHandler(_(u'button_add_path', default=u'Add path')) def handleAdd(self, action): portal = self.context.portal_url.getPortalObject() config = IConfig(portal) data, errors = self.extractData() if not len(errors): path = data.get('path').strip() if not path.startswith('/'): raise Exception('Path does not start with /') config.appendPathToBlacklist(path) message = _(u'info_path_added', default=u'Path added') IStatusMessage(self.request).addStatusMessage(message, type='info') return self.request.RESPONSE.redirect( './@@publisher-config-blacklist')
def handleAdd(self, action): portal = self.context.portal_url.getPortalObject() config = IConfig(portal) data, errors = self.extractData() if not len(errors): path = data.get('path').strip() if not path.startswith('/'): raise Exception('Path does not start with /') config.appendPathToBlacklist(path) message = _(u'info_path_added', default=u'Path added') IStatusMessage(self.request).addStatusMessage(message, type='info') return self.request.RESPONSE.redirect( './@@publisher-config-blacklist')
def __call__(self, no_response=False, msg=None, *args, **kwargs): """ Add the current context as delete-job to the queue, creates a status message to inform the user and returns to the default view. @param args: list of unnamed arguments @type args: list @param kwargs: dict of named keyword-arguments @type kwargs: dict @return: Redirect to object`s default view """ if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning('Could not create delete job for blacklisted ' 'object (%s at %s)' % ( self.context.Title(), '/'.join(self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False # This view should not be executed at the PloneSiteRoot if IPloneSiteRoot.providedBy(self.context): raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) queue.createJob('delete', self.context, username) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'delete', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) # status message if msg is None: msg = _(u'This object will be deleted at the remote sites.') add_transaction_aware_status_message(self.request, msg, type='info') if not no_response: return self.request.RESPONSE.redirect('./view')
def __call__(self, no_response=False, msg=None, recursive=True): if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning( 'Could not create push job for blacklisted object (%s at %s)' % (self.context.Title(), '/'.join( self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False event.notify(BeforePublishEvent(self.context)) # mle: now its possible to execite this view on plonesiteroot # This View should not be executed at the PloneSiteRoot # if IPloneSiteRoot.providedBy(self.context): # raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) queue.createJob('push', self.context, username) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'push', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) if recursive and base_hasattr(self.context, 'contentValues'): # Use contentValues for implicit ftw.trash compatibility. for obj in filter(belongs_to_parent, self.context.contentValues()): obj.restrictedTraverse('@@publisher.publish')(no_response=True, msg=msg) # status message if msg is None: msg = _(u'This object has been added to the queue.') IStatusMessage(self.request).addStatusMessage(msg, type='info') if not no_response: return self.request.RESPONSE.redirect('./view')
def handleAdd(self, action): portal = self.context.portal_url.getPortalObject() config = IConfig(portal) data, errors = self.extractData() if not len(errors): path = data.get('path').strip() if not path.startswith('/'): raise Exception('Path does not start with /') config.appendPathToBlacklist(path) message = _(u'info_path_added', default=u'Path added') IStatusMessage(self.request).addStatusMessage( message, type='info') return self.request.RESPONSE.redirect('./@@publisher-config-blacklist')
def __call__(self, no_response=False, msg=None): if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning('Could not create delete job for blacklisted ' 'object (%s at %s)' % (self.context.Title(), '/'.join( self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False # This view should not be executed at the PloneSiteRoot if IPloneSiteRoot.providedBy(self.context): raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) queue.createJob('delete', self.context, username) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'delete', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) # status message if msg is None: msg = _(u'This object will be deleted at the remote sites.') add_transaction_aware_status_message(self.request, msg, type='info') if not no_response: return self.request.RESPONSE.redirect('./view')
def download(self, REQUEST=None, RESPONSE=None): """Download the saved data """ url_tool = getToolByName(self, 'portal_url') config = IConfig(url_tool.getPortalObject()) pub_state = getMultiAdapter((self, REQUEST), IPublisherContextState) realms = config.getRealms() download_format = getattr(self, 'DownloadFormat', 'csv') if len(realms) == 0 or not pub_state.is_parent_published(): if download_format == 'tsv': return self.download_tsv(REQUEST, RESPONSE) else: assert download_format == 'csv', 'Unknown download format' return self.download_csv(REQUEST, RESPONSE) elif len(realms) == 1: data = {'uid': self.UID(), 'download_format': download_format} return_data_realm = sendRequestToRealm(data, realms[0], 'formgen_get_saved_data') return_data_this = self.getSavedFormInputForEdit() return_data = '{}{}'.format(return_data_realm, return_data_this) filename = self.id if filename.find('.') < 0: filename = '%s.%s' % (filename, download_format) header_value = contentDispositionHeader('attachment', self.getCharset(), filename=filename) RESPONSE.setHeader("Content-Disposition", header_value) sep_type = download_format == 'csv' and 'comma' or 'tab' RESPONSE.setHeader("Content-Type", 'text/%s-separated-values;' 'charset=%s' % (sep_type, self.getCharset())) return return_data else: messages = IStatusMessage(self.request) messages.add(_(u"couldn't determine correct realm to fetch from."), type=u"error") return RESPONSE.redirect(self.context.absolute_url())
def download(self, REQUEST=None, RESPONSE=None): """Download the saved data """ url_tool = getToolByName(self, 'portal_url') config = IConfig(url_tool.getPortalObject()) pub_state = getMultiAdapter((self, REQUEST), IPublisherContextState) realms = config.getRealms() download_format = getattr(self, 'DownloadFormat', 'csv') if len(realms) == 0 or not pub_state.is_parent_published(): if download_format == 'tsv': return self.download_tsv(REQUEST, RESPONSE) else: assert download_format == 'csv', 'Unknown download format' return self.download_csv(REQUEST, RESPONSE) elif len(realms) == 1: data = {'uid': self.UID(), 'download_format': download_format} return_data = sendRequestToRealm(data, realms[0], 'formgen_get_saved_data') filename = self.id if filename.find('.') < 0: filename = '%s.%s' % (filename, download_format) header_value = contentDispositionHeader('attachment', self.getCharset(), filename=filename) RESPONSE.setHeader("Content-Disposition", header_value) sep_type = download_format == 'csv' and 'comma' or 'tab' RESPONSE.setHeader("Content-Type", 'text/%s-separated-values;' 'charset=%s' % (sep_type, self.getCharset())) return return_data else: messages = IStatusMessage(self.request) messages.add(_(u"couldn't determine correct realm to fetch from."), type=u"error") return RESPONSE.redirect(self.context.absolute_url())
from z3c.form import field from z3c.form import form from z3c.form import interfaces from zope.component import getUtility from zope.interface import implements from zope.publisher.interfaces import Retry import datetime import md5 EXECUTED_JOBS_BATCH_SIZE = 100 # lets translate the actions with i18ndude TRANSLATED_ACTIONS = { 'push': _(u'action_push', default=u'Push'), 'move': _(u'action_move', default=u'Move'), 'delete': _(u'action_delete', default=u'Delete'), } # -- Forms class CreateRealmForm(form.Form): """ The CreateRealmForm is a z3c-form used for adding a new Realm instance to the publisher configuration. @cvar fields: fields from the schema IRealmSchema @cvar ignoreContext: do not use context (z3c-form setting) @cvar label: label of the form
def _get_data(self): columns = dict(ListExecutedJobs.COLUMNS) i18n_details = self.context.translate(_( u'link_job_details', default=u'Details')) i18n_requeu = self.context.translate(_( u'link_requeue_job', default='Requeue')) # get a batched part of the executed jobs. But we need to start # batching at the end, get the batch forward and then reverse, # because we want the newest job at the top. b_start = int(self.request.get('b_start', 0)) jobs_length = self.queue.get_executed_jobs_length() end = jobs_length - b_start start = end - EXECUTED_JOBS_BATCH_SIZE if start < 0: start = 0 entries = list(self.queue.get_executed_jobs(start, end)) entries.reverse() for key, job in entries: state = job.get_latest_executed_entry() state_name = getattr(state, 'localized_name', None) if state_name: state_name = self.context.translate(state_name) else: state_name = state.__class__.__name__ if isinstance(state, states.ErrorState): colored_state = '<span class="error" style="color:red;">' +\ '%s</span>' % self.context.translate(state_name) elif isinstance(state, states.WarningState): colored_state = '<span class="error" style="color:orange;">' +\ '%s</span>' % self.context.translate(state_name) else: colored_state = '<span class="success">%s</span>' % state_name date = 'unknown' try: date = job.executed_list[-1]['date'].strftime('%d.%m.%Y %H:%M') except (ConflictError, Retry): raise except: pass ctrl = ' '.join(( '<a href="./@@publisher-config-executed-job-details' +\ '?job=%s">%s</a>' % (key, i18n_details), '|', '<a href="./@@publisher-config-listExecutedJobs' +\ '?requeue.job=%s">%s</a>' % (key, i18n_requeu), )) shortened_title = job.objectTitle maximum_length = 35 if len(shortened_title) > maximum_length: try: shortened_title = shortened_title.decode('utf8') shortened_title = shortened_title[:maximum_length] + \ u' ...' shortened_title = shortened_title.encode('utf8') except (ConflictError, Retry): raise except: pass yield { columns['date']: date, columns['title']: '<a href="%s" title="%s">%s</a>' % ( job.objectPath + '/view', job.objectTitle, shortened_title), columns['action']: TRANSLATED_ACTIONS.get(job.action, job.action), columns['state']: colored_state, columns['username']: job.username, columns['']: ctrl, }
from zope.interface import implements from zope.publisher.interfaces import Retry import datetime import md5 try: from plone.protect.interfaces import IDisableCSRFProtection except ImportError: IDisableCSRFProtection = None EXECUTED_JOBS_BATCH_SIZE = 100 # lets translate the actions with i18ndude TRANSLATED_ACTIONS = { 'push': _(u'action_push', default=u'Push'), 'move': _(u'action_move', default=u'Move'), 'delete': _(u'action_delete', default=u'Delete'), } # -- Forms class CreateRealmForm(form.Form): """ The CreateRealmForm is a z3c-form used for adding a new Realm instance to the publisher configuration. @cvar fields: fields from the schema IRealmSchema @cvar ignoreContext: do not use context (z3c-form setting) @cvar label: label of the form
class EditRealmForm(form.EditForm): """ The EditRealmForm is used for editing a Realm object. @cvar fields: fields from the schema IRealmSchema @cvar ignoreContext: do not use context (z3c-form setting) @cvar label: label of the form """ implements(IWrappedForm) fields = field.Fields(IEditRealmSchema) ignoreContext = True label = u'Edit Realm' def updateWidgets(self): """ Updates the widgets (z3c-form method). Customized for adding a HIDDEN_MODE-Flag to the ID-field. """ super(EditRealmForm, self).updateWidgets() self.widgets['id'].mode = interfaces.HIDDEN_MODE @button.buttonAndHandler(_(u'button_save_realm', default=u'Save Realm')) def handleSave(self, action): """ """ data, errors = self.extractData() config = IConfig(self.context) assert config.is_update_realms_possible() if len(errors)==0: # get realm currentRealm = self.getRealmById(data['id']) if not currentRealm: raise Exception('Could not find realm') # no other realm should have same url+username for realm in config.getRealms(): if realm!=currentRealm: if realm.username==data['username'] and\ realm.url==data['url']: self.statusMessage( 'This URL / Username combination already exists!', ) return # update realm currentRealm.active = data['active'] and 1 or 0 currentRealm.url = data['url'] currentRealm.username = data['username'] if data['password'] and len(data['password'])>0: currentRealm.password = data['password'] self.statusMessage('Updated realm successfully') return self.request.RESPONSE.redirect('./@@publisher-config') def statusMessage(self, message, type='info'): IStatusMessage(self.request).addStatusMessage( message, type=type) def makeRealmId(self, realm): return md5.md5('%s-%s' % (realm.url, realm.username)).hexdigest() def getRealmById(self, id): for realm in IConfig(self.context).getRealms(): if self.makeRealmId(realm)==id: return realm return None
def __call__(self, event, no_response=False, msg=None, *args, **kwargs): """ Creates a "rename" job for the current item(s) @param args: list of unnamed arguments @type args: list @param kwargs: dict of named keyword-arguments @type kwargs: dict @return: Redirect to object`s default view """ if IPreventPublishing.providedBy(self.context): return 'prevented' if publisher_jobs_are_disabled(): return 'disabled' self.logger = getLogger() # is the object blacklisted? if IPathBlacklist(self.context).is_blacklisted(): self.logger.warning('Could not create move job for blacklisted '+\ 'object (%s at %s)' % ( self.context.Title(), '/'.join(self.context.getPhysicalPath()))) if not no_response: return self.request.RESPONSE.redirect('./view') return False # This View should not be executed at the PloneSiteRoot if IPloneSiteRoot.providedBy(self.context): raise Exception('Not allowed on PloneSiteRoot') # get username user = self.context.portal_membership.getAuthenticatedMember() username = user.getUserName() # create Job portal = self.context.portal_url.getPortalObject() queue = IQueue(portal) additional_data = {'move_data': { 'newName': event.newName, 'newParent': get_site_relative_path(event.newParent), 'newTitle': event.object.Title().decode('utf-8'), 'oldName': event.oldName, 'oldParent': get_site_relative_path(event.oldParent), }} queue.createJob('move', self.context, username, additional_data=additional_data) self.logger.debug('Created "%s" Job for "%s" at %s' % ( 'move', self.context.Title(), '/'.join(self.context.getPhysicalPath()), )) # status message if msg is None: msg = _(u'Object move/rename action has been added to the queue.') IStatusMessage(self.request).addStatusMessage( msg, type='info' ) if not no_response: return self.request.RESPONSE.redirect('./view')
def get_clear_confirm_message(self): """Translated confirm message for clearing the queue """ return self.context.translate(_( u'confirm_clear_queue', default=u'Are you sure to delete all jobs in the queue?'))
class ListExecutedJobs(PublisherConfigletView): COLUMNS = (('date', _(u'th_date', default=u'Date')), ('title', _(u'th_title', default=u'Title')), ('action', _(u'th_action', default=u'Action')), ('state', _(u'th_state', default=u'State')), ('username', _(u'th_username', default=u'Username')), ('', '')) actions_template = ViewPageTemplateFile('exectued_list_actions.pt') def __call__(self, *args, **kwargs): redirect = False if ('button.cleanup' in self.request or 'button.delete.olderthan' in self.request or 'requeue.job' in self.request): redirect = self.button_action() if redirect: url = './@@publisher-config-listExecutedJobs' return self.request.RESPONSE.redirect(url) # BATCH # create a fake iterable object with the length of all objects, # but we dont want to load them all.. fake_data = xrange(self.queue.get_executed_jobs_length()) b_start = int(self.request.get('b_start', 0)) self.batch = Batch(fake_data, EXECUTED_JOBS_BATCH_SIZE, b_start) return super(ListExecutedJobs, self).__call__(*args, **kwargs) @protect(PostOnly) @protect(CheckAuthenticator) def button_action(self, REQUEST=None): if self.request.get('button.cleanup'): self.queue.clear_executed_jobs() return True if self.request.get('button.delete.olderthan'): days = int(self.request.get('days')) date = datetime.datetime.now() - datetime.timedelta(days) self.queue.remove_executed_jobs_older_than(date) return True requeueJob = self.request.get('requeue.job') if requeueJob: key = int(requeueJob) try: job = self.queue.get_executed_job_by_key(key) except KeyError: # could not find job pass else: self.queue.remove_executed_job(key) job.move_jsonfile_to(self.config.getDataFolder()) self.queue.appendJob(job) return True def render_table(self): generator = getUtility(ITableGenerator, 'ftw.tablegenerator') columns = [c[1] for c in ListExecutedJobs.COLUMNS] return generator.generate(self._get_data(), columns) def _get_data(self): columns = dict(ListExecutedJobs.COLUMNS) # get a batched part of the executed jobs. But we need to start # batching at the end, get the batch forward and then reverse, # because we want the newest job at the top. b_start = int(self.request.get('b_start', 0)) jobs_length = self.queue.get_executed_jobs_length() end = jobs_length - b_start start = end - EXECUTED_JOBS_BATCH_SIZE if start < 0: start = 0 entries = list(self.queue.get_executed_jobs(start, end)) entries.reverse() for key, job in entries: state = job.get_latest_executed_entry() state_name = getattr(state, 'localized_name', None) if state_name: state_name = self.context.translate(state_name) else: state_name = state.__class__.__name__ if isinstance(state, states.ErrorState): colored_state = '<span class="error" style="color:red;">' +\ '%s</span>' % self.context.translate(state_name) elif isinstance(state, states.WarningState): colored_state = '<span class="error" style="color:orange;">' +\ '%s</span>' % self.context.translate(state_name) else: colored_state = '<span class="success">%s</span>' % state_name date = 'unknown' try: date = job.executed_list[-1]['date'].strftime('%d.%m.%Y %H:%M') except (ConflictError, Retry): raise except: pass ctrl = self.actions_template(key=key) shortened_title = job.objectTitle maximum_length = 35 if len(shortened_title) > maximum_length: try: shortened_title = shortened_title.decode('utf8') shortened_title = shortened_title[:maximum_length] + \ u' ...' shortened_title = shortened_title.encode('utf8') except (ConflictError, Retry): raise except: pass yield { columns['date']: date, columns['title']: '<a href="%s" title="%s">%s</a>' % ( job.objectPath + '/view', job.objectTitle, shortened_title), columns['action']: TRANSLATED_ACTIONS.get(job.action, job.action), columns['state']: colored_state, columns['username']: job.username, columns['']: ctrl, } def get_translated_cleanup_prompt(self): """Returns the converted prompt string which is displayed when clicking on "cleanup". """ return self.context.translate(_( u'prompt_cleanup', default=u'Are you shure to delete all executed jobs?'))
class IBlacklistPathSchema(Interface): path = schema.TextLine(title=_(u'label_path', default=u'Path'), required=True)
class IEditRealmSchema(IRealmSchema): id = schema.TextLine(title=u'id') password = schema.Password(title=_(u'label_realm_password', u'Password'), required=False)