class NyTalkBackConsultation(Implicit, NyContentData, NyContentType, NyAttributes, NyProperties, NyRoleManager, NyContainer, NyNonCheckControl, utils, NyFolderBase): """ """ meta_type = METATYPE_TALKBACKCONSULTATION meta_label = LABEL_OBJECT meta_types = [ { 'name': METATYPE_TALKBACKCONSULTATION_SECTION, 'action': 'addSection', 'permission': PERMISSION_MANAGE_TALKBACKCONSULTATION }, ] icon = 'misc_/NaayaContent/NyTalkBackConsultation.gif' icon_marked = 'misc_/NaayaContent/NyTalkBackConsultation_marked.gif' security = ClassSecurityInfo() edit_access = NyAccess( 'edit_access', { PERMISSION_REVIEW_TALKBACKCONSULTATION: "Submit comments", PERMISSION_MANAGE_TALKBACKCONSULTATION: "Administer consultation", PERMISSION_INVITE_TO_TALKBACKCONSULTATION: "Send invitations", }) section_sort_order = tuple() def __init__(self, id, contributor): """ """ self.id = id self.contributor = contributor NyContainer.__dict__['__init__'](self) NyProperties.__dict__['__init__'](self) self.invitations = InvitationsContainer('invitations') self.submitted = 1 def set_allow_reviewer_invites(self, allow): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) if allow and 'Reviewer' not in roles: new_roles = roles + ['Reviewer'] elif not allow and 'Reviewer' in roles: new_roles = tuple(role for role in roles if role != 'Reviewer') else: new_roles = roles if new_roles: setattr(self, perm, new_roles) else: if hasattr(self, perm): delattr(self, perm) def get_allow_reviewer_invites(self): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) return ('Reviewer' in roles) allow_reviewer_invites = property(get_allow_reviewer_invites, set_allow_reviewer_invites) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'saveProperties') def saveProperties(self, REQUEST=None, **kwargs): """ """ if not self.checkPermissionEditObject(): raise EXCEPTION_NOTAUTHORIZED(EXCEPTION_NOTAUTHORIZED_MSG) if REQUEST is not None: schema_raw_data = dict(REQUEST.form) else: schema_raw_data = kwargs _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None)) _releasedate = self.process_releasedate( schema_raw_data.pop('releasedate', ''), self.releasedate) form_errors = self.process_submitted_form( schema_raw_data, _lang, _override_releasedate=_releasedate) if not form_errors: self._p_changed = True self.recatalogNyObject(self) # log date contributor = self.REQUEST.AUTHENTICATED_USER.getUserName() auth_tool = self.getAuthenticationTool() auth_tool.changeLastPost(contributor) notify(NyContentObjectEditEvent(self, contributor)) if REQUEST: self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: if REQUEST is not None: self._prepare_error_response(REQUEST, form_errors, schema_raw_data) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: raise ValueError(form_errors.popitem()[1]) # pick an error security.declareProtected(view, 'get_consultation') def get_consultation(self): return self security.declareProtected(view, 'list_sections') def list_sections(self): """ """ metatypes = [METATYPE_TALKBACKCONSULTATION_SECTION] sections = dict(self.objectItems(metatypes)) output = [] for section_id in self.section_sort_order: if section_id in sections: output.append(sections.pop(section_id)) output.extend( sorted(sections.values(), key=operator.attrgetter('title'))) return output security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'save_sort_order') def save_sort_order(self, sort_section_id, REQUEST=None): """ save the sort order of sections """ self.section_sort_order = tuple(sort_section_id) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) _comments_atom = NaayaPageTemplateFile('zpt/comments_atom', globals(), 'tbconsultation_comments_atom') security.declareProtected(view, 'comments_atom') def comments_atom(self, REQUEST=None, days=2): """ ATOM feed with consultation comments """ if isinstance(days, basestring): try: days = int(days) except: days = 2 cutoff = DateTime() - days comments_list = [] for section in self.list_sections(): for paragraph in section.get_paragraphs(): for comment in paragraph.get_comments(): if comment.comment_date < cutoff: continue comments_list.append(comment) comments_list.sort(key=operator.attrgetter('comment_date'), reverse=True) if comments_list: feed_updated = comments_list[0].comment_date else: feed_updated = DateTime() def atom_date_format(date): d = DT2dt(date).strftime('%Y-%m-%dT%H:%M:%S%z') return d[:-2] + ':' + d[-2:] if REQUEST is not None: REQUEST.RESPONSE.setHeader('Content-Type', 'application/atom+xml') return self._comments_atom(comments_list=comments_list, atom_date_format=atom_date_format, feed_updated=feed_updated) security.declareProtected(view, 'get_start_date') def get_start_date(self): """ Returns the start date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.start_date) security.declareProtected(view, 'get_end_date') def get_end_date(self): """ Returns the end date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.end_date) security.declareProtected(view, 'get_days_left') def get_days_left(self): """ Returns the remaining days for the consultation or the number of days before it starts """ today = self.utGetTodayDate().earliestTime() if not self.start_date or not self.end_date: return (1, 0) after_end_date = self.end_date + 1 if self.start_date.lessThanEqualTo(today): return (1, int(str(after_end_date - today).split('.')[0])) else: return (0, int(str(self.start_date - today).split('.')[0])) security.declareProtected(view_management_screens, 'manage_options') def manage_options(self): """ """ l_options = (NyContainer.manage_options[0], ) l_options += ({'label': 'View', 'action': 'index_html'},) + \ NyContainer.manage_options[3:8] return l_options security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'delete_sections') def delete_sections(self, del_section_id, REQUEST=None): """ remove the specified sections """ self.manage_delObjects(list(del_section_id)) self.setSessionInfoTrans('Removed ${count} sections.', count=str(len(del_section_id))) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) def get_user_name(self): # first, check if we have an invite key invitation = self.invitations.get_current_invitation(self.REQUEST) if invitation is not None: return invitation.name # no invite key; look for current Zope user auth_tool = self.getAuthenticationTool() userid = auth_tool.get_current_userid() if userid is None: # anonymous user return None name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def get_user_name_or_userid(self, userid=None): if userid is None: return self.get_user_name() auth_tool = self.getAuthenticationTool() name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def checkTalkBackConsultationUser(self): """ Checks if the user is logged in and has reviewer rights: 0 if user is anonymous, 1 if user has reviewer role 2 if user doesn't have reviewer role """ review_check = self.checkPermissionReviewTalkBackConsultation() if self.isAnonymousUser(): return 0 elif review_check: return 1 elif not review_check: return 2 security.declareProtected(view, 'check_cannot_comment') def check_cannot_comment(self): """ """ if not self.checkPermissionReviewTalkBackConsultation(): if self.isAnonymousUser(): return 'not-logged-in' else: return 'no-permission' if self.get_days_left()[1] <= 0 and not ( self.checkPermissionManageTalkBackConsultation() or self.checkPermissionReviewTalkBackConsultationAfterDeadline()): return 'deadline-reached' security.declareProtected(PERMISSION_COMMENTS_ADD, 'log_in_authenticated') def log_in_authenticated(self, REQUEST=None): """ Log in user and redirect to TalkBack Consultation index """ if REQUEST is not None: self.REQUEST.RESPONSE.redirect(self.absolute_url()) # permissions def checkPermissionReviewTalkBackConsultation(self): """ Check for reviewing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_REVIEW_TALKBACKCONSULTATION) security.declareProtected( PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE, 'review_after_deadline') def review_after_deadline(self): """ Dummy function to register the permission. """ raise NotImplementedError def checkPermissionReviewTalkBackConsultationAfterDeadline(self): """ Check for reviewing the TalkBack Consultation after the deadline has passed. """ return self.checkPermission( PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE) def checkPermissionManageTalkBackConsultation(self): """ Check for managing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_MANAGE_TALKBACKCONSULTATION) def checkPermissionInviteToTalkBackConsultation(self): """ Check for inviting others to the TalkBack Consultation. """ return self.checkPermission(PERMISSION_INVITE_TO_TALKBACKCONSULTATION) def own_comments(self): """ Check if the current user has any comments on the consultation """ return [ comment for comment in self.admin_comments._all_comments() if comment['comment'].contributor == self.REQUEST.AUTHENTICATED_USER.getUserName() ] security.declareProtected(view, 'custom_editor') def custom_editor(self, editor_tool, lang, dom_id): extra_options = { 'content_css': self.absolute_url() + '/misc_/NaayaContent/tb-editor.css', 'theme_advanced_buttons1': 'bold,italic,underline,strikethrough,sub,sup,forecolor,' 'backcolor,removeformat,separator,' 'bullist,numlist,separator,' 'justifyleft,justifycenter,justifyright,justifyfull,separator,' 'link,unlink,hr,image,separator,' 'pastetext,pasteword,cleanup,code,help', 'theme_advanced_buttons2': '', } return editor_tool.render(dom_id, lang, image_support=True, extra_options=extra_options) security.declareProtected(view, 'get_files') def get_files(self): """ Get a list of all files attached to the consultation""" return [ob for ob in self.objectValues('Naaya Blob File')] security.declareProtected(view, 'findUsers') def findUsers(self, search_param, search_term): """ """ if len(search_term) == 0: return [] return json.dumps(findUsers(self.getSite(), search_param, search_term)) addSection = addSection # zmi pages security.declareProtected(view_management_screens, 'manage_edit_html') manage_edit_html = PageTemplateFile('zpt/talkbackconsultation_manage_edit', globals()) # site pages security.declareProtected(view, 'index_html') index_html = NaayaPageTemplateFile('zpt/talkbackconsultation_index', globals(), 'tbconsultation_index') # standard_template_macro, header and footer templates are proxied # since invited reviewers have "View" permission only in this folder; # if the consultation is restricted, they would not be able to see # consultation pages. def standard_html_header(self, *args, **kwargs): return self.aq_parent.standard_html_header(*args, **kwargs) def standard_html_footer(self, *args, **kwargs): return self.aq_parent.standard_html_footer(*args, **kwargs) def standard_template_macro(self, *args, **kwargs): return self.aq_parent.standard_template_macro(*args, **kwargs) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'edit_html') edit_html = NaayaPageTemplateFile('zpt/talkbackconsultation_edit', globals(), 'tbconsultation_edit') security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'section_add_html') section_add_html = addSection_html __allow_groups__ = InvitationUsersTool() _View_Permission = ['InvitedReviewer'] _Naaya___Review_TalkBack_Consultation_Permission = ['InvitedReviewer'] __ac_roles__ = ['InvitedReviewer'] admin_comments = CommentsAdmin('admin_comments')
class NyTalkBackConsultation(NyAttributes, Implicit, NyProperties, NyContainer, NyNonCheckControl, utils): """ """ meta_type = METATYPE_TALKBACKCONSULTATION meta_label = LABEL_OBJECT meta_types = [ { 'name': METATYPE_TALKBACKCONSULTATION_SECTION, 'action': 'addSection', 'permission': PERMISSION_MANAGE_TALKBACKCONSULTATION }, ] icon = 'misc_/NaayaContent/NyTalkBackConsultation.gif' icon_marked = 'misc_/NaayaContent/NyTalkBackConsultation_marked.gif' title = LocalProperty('title') description = LocalProperty('description') security = ClassSecurityInfo() def __init__(self, id, title, description, sortorder, start_date, end_date, public_registration, allow_file, allow_reviewer_invites, contributor, releasedate, lang): """ """ self.id = id self.contributor = contributor NyContainer.__dict__['__init__'](self) self.save_properties(title, description, sortorder, start_date, end_date, public_registration, allow_file, allow_reviewer_invites, releasedate, lang) NyProperties.__dict__['__init__'](self) self.invitations = InvitationsContainer('invitations') self.submitted = 1 def set_allow_reviewer_invites(self, allow): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) if allow and 'Reviewer' not in roles: roles.append('Reviewer') elif not allow and 'Reviewer' in roles: roles.remove('Reviewer') if roles: setattr(self, perm, roles) else: if hasattr(self, perm): delattr(self, perm) def get_allow_reviewer_invites(self): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) return ('Reviewer' in roles) allow_reviewer_invites = property(get_allow_reviewer_invites, set_allow_reviewer_invites) security.declarePrivate('save_properties') def save_properties(self, title, description, sortorder, start_date, end_date, public_registration, allow_file, allow_reviewer_invites, releasedate, lang): self._setLocalPropValue('title', lang, title) self._setLocalPropValue('description', lang, description) if not hasattr(self, 'imageContainer'): self.imageContainer = NyImageContainer(self, True) if start_date: self.start_date = self.utConvertStringToDateTimeObj(start_date) else: self.start_date = self.utGetTodayDate() if end_date: self.end_date = self.utConvertStringToDateTimeObj(end_date) else: self.end_date = self.utGetTodayDate() try: self.sortorder = abs(int(sortorder)) except: self.sortorder = DEFAULT_SORTORDER self.releasedate = releasedate self.public_registration = public_registration self.allow_file = allow_file self.allow_reviewer_invites = allow_reviewer_invites security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'saveProperties') def saveProperties(self, title='', description='', sortorder='', start_date='', end_date='', public_registration='', allow_file='', allow_reviewer_invites=False, lang='', REQUEST=None): """ """ if not title: self.setSession('title', title) self.setSession('description', description) self.setSessionErrors(['The Title field must have a value.']) if REQUEST: return REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), lang)) else: raise ValueError('The title field must have a value.') releasedate = self.releasedate self.updateRequestRoleStatus(public_registration, lang) self.save_properties(title, description, sortorder, start_date, end_date, public_registration, allow_file, allow_reviewer_invites, releasedate, lang) if REQUEST: self.setSessionInfo([MESSAGE_SAVEDCHANGES % self.utGetTodayDate()]) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), lang)) security.declarePrivate('addDynProp') def addDynProp(self): """ """ dynprop_tool = self.getDynamicPropertiesTool() if not hasattr(dynprop_tool, METATYPE_OBJECT): dynprop_tool.manage_addDynamicPropertiesItem(id=METATYPE_OBJECT, title=METATYPE_OBJECT) dynprop_tool._getOb(METATYPE_OBJECT).manageAddDynamicProperty( id='show_contributor_request_role', name= 'Allow visitors to register as reviewers for this consultation', type='boolean') security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'updateRequestRoleStatus') def updateRequestRoleStatus(self, public_registration, lang): """ Allow public registration for this consultation """ if public_registration: self.updateDynamicProperties( self.processDynamicProperties( METATYPE_TALKBACKCONSULTATION, {'show_contributor_request_role': 'on'}), lang) if not public_registration: self.updateDynamicProperties( self.processDynamicProperties( METATYPE_TALKBACKCONSULTATION, {'show_contributor_request_role': ''}), lang) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'checkReviewerRole') def checkReviewerRole(self): """ Checks if the 'Reviewer' role exists, creates and adds review permissions if it doesn't exist """ auth_tool = self.getAuthenticationTool() roles = auth_tool.list_all_roles() PERMISSION_GROUP = 'Review content' if PERMISSION_GROUP not in auth_tool.listPermissions().keys(): auth_tool.addPermission( PERMISSION_GROUP, 'Allow posting reviews/comments to consultation objects.', [PERMISSION_REVIEW_TALKBACKCONSULTATION]) else: permissions = auth_tool.getPermission(PERMISSION_GROUP).get( 'permissions', []) if PERMISSION_REVIEW_TALKBACKCONSULTATION not in permissions: permissions.append(PERMISSION_REVIEW_TALKBACKCONSULTATION) auth_tool.editPermission( PERMISSION_GROUP, 'Allow posting reviews/comments to consultation objects.', permissions) if 'Reviewer' not in roles: auth_tool.addRole('Reviewer', [PERMISSION_GROUP]) else: role_permissions = auth_tool.getRolePermissions('Reviewer') if PERMISSION_GROUP not in role_permissions: role_permissions.append(PERMISSION_GROUP) auth_tool.editRole('Reviewer', role_permissions) #give permissions to administrators and reviewers admin_permissions = self.permissionsOfRole('Administrator') site = self.getSite() if PERMISSION_MANAGE_TALKBACKCONSULTATION not in admin_permissions: site.manage_permission(PERMISSION_MANAGE_TALKBACKCONSULTATION, ('Administrator', ), acquire=1) site.manage_permission(PERMISSION_REVIEW_TALKBACKCONSULTATION, ( 'Administrator', 'Reviewer', ), acquire=1) security.declareProtected(view, 'get_consultation') def get_consultation(self): return self security.declareProtected(view, 'list_sections') def list_sections(self): """ """ sections = self.objectValues([METATYPE_TALKBACKCONSULTATION_SECTION]) sections.sort(lambda x, y: cmp(x.title, y.title)) return sections security.declareProtected(view, 'get_start_date') def get_start_date(self): """ Returns the start date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.start_date) security.declareProtected(view, 'get_end_date') def get_end_date(self): """ Returns the end date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.end_date) security.declareProtected(view, 'get_days_left') def get_days_left(self): """ Returns the remaining days for the consultation or the number of days before it starts """ today = self.utGetTodayDate().earliestTime() if not self.start_date or not self.end_date: return (1, 0) if self.start_date.lessThanEqualTo(today): return (1, int(str(self.end_date - today).split('.')[0])) else: return (0, int(str(self.start_date - today).split('.')[0])) security.declareProtected(view_management_screens, 'manage_options') def manage_options(self): """ """ l_options = (NyContainer.manage_options[0], ) l_options += ({'label': 'View', 'action': 'index_html'},) + \ NyContainer.manage_options[3:8] return l_options def get_user_name(self): # first, check if we have an invite key invitation = self.invitations.get_current_invitation(self.REQUEST) if invitation is not None: return invitation.name # no invite key; look for current Zope user auth_tool = self.getAuthenticationTool() userid = auth_tool.get_current_userid() if userid is None: # anonymous user return None name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def get_user_name_or_userid(self, userid=None): if userid is None: return self.get_user_name() auth_tool = self.getAuthenticationTool() name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def checkTalkBackConsultationUser(self): """ Checks if the user is logged in and has reviewer rights: 0 if user is anonymous, 1 if user has reviewer role 2 if user doesn't have reviewer role """ review_check = self.checkPermissionReviewTalkBackConsultation() if self.isAnonymousUser(): return 0 elif review_check: return 1 elif not review_check: return 2 security.declareProtected(view, 'check_cannot_comment') def check_cannot_comment(self): """ """ if not self.checkPermissionReviewTalkBackConsultation(): return 'no-permission' if self.get_days_left()[1] <= 0: return 'deadline-reached' security.declareProtected(PERMISSION_COMMENTS_ADD, 'log_in_authenticated') def log_in_authenticated(self, REQUEST=None): """ Log in user and redirect to TalkBack Consultation index """ if REQUEST is not None: self.REQUEST.RESPONSE.redirect(self.absolute_url()) #permissions def checkPermissionReviewTalkBackConsultation(self): """ Check for reviewing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_REVIEW_TALKBACKCONSULTATION) def checkPermissionManageTalkBackConsultation(self): """ Check for managing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_MANAGE_TALKBACKCONSULTATION) def checkPermissionInviteToTalkBackConsultation(self): """ Check for inviting others to the TalkBack Consultation. """ return self.checkPermission(PERMISSION_INVITE_TO_TALKBACKCONSULTATION) security.declareProtected(view, 'custom_editor') def custom_editor(self, editor_tool, lang, dom_id): extra_options = { 'content_css': [self.absolute_url() + '/misc_/NaayaContent/tb-editor.css'], 'theme_advanced_buttons1': [ 'bold,italic,underline,strikethrough,sub,sup,forecolor,' 'backcolor,removeformat,separator,' 'bullist,numlist,separator,' 'justifyleft,justifycenter,justifyright,justifyfull,separator,' 'link,unlink,hr,image,separator,' 'pastetext,pasteword,cleanup,code,help' ], 'theme_advanced_buttons2': [''], } return editor_tool.render(dom_id, lang, image_support=True, extra_options=extra_options) addSection = addSection #zmi pages security.declareProtected(view_management_screens, 'manage_edit_html') manage_edit_html = PageTemplateFile('zpt/talkbackconsultation_manage_edit', globals()) #site pages security.declareProtected(view, 'index_html') index_html = NaayaPageTemplateFile('zpt/talkbackconsultation_index', globals(), 'tbconsultation_index') # header and footer templates are proxied since invited reviewers # have "View" permission only in this folder; if the consultation # is restricted, they would not be able to see consultation pages. def standard_html_header(self, *args, **kwargs): return self.aq_parent.standard_html_header(*args, **kwargs) def standard_html_footer(self, *args, **kwargs): return self.aq_parent.standard_html_footer(*args, **kwargs) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'edit_html') edit_html = PageTemplateFile('zpt/talkbackconsultation_edit', globals()) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'section_add_html') section_add_html = addSection_html __allow_groups__ = InvitationUsersTool() _View_Permission = ['InvitedReviewer'] _Naaya___Review_TalkBack_Consultation_Permission = ['InvitedReviewer'] __ac_roles__ = ['InvitedReviewer'] admin_comments = CommentsAdmin('admin_comments')
class NyTalkBackConsultation(Implicit, NyContentData, NyContentType, NyAttributes, NyProperties, NyRoleManager, NyContainer, NyNonCheckControl, utils, NyFolderBase): """ """ meta_type = METATYPE_TALKBACKCONSULTATION meta_label = LABEL_OBJECT meta_types = [ {'name': METATYPE_TALKBACKCONSULTATION_SECTION, 'action': 'addSection', 'permission': PERMISSION_MANAGE_TALKBACKCONSULTATION}, ] icon = 'misc_/NaayaContent/NyTalkBackConsultation.gif' icon_marked = 'misc_/NaayaContent/NyTalkBackConsultation_marked.gif' security = ClassSecurityInfo() edit_access = NyAccess('edit_access', { PERMISSION_REVIEW_TALKBACKCONSULTATION: "Submit comments", PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE: "Submit comments after deadline", PERMISSION_MANAGE_TALKBACKCONSULTATION: "Administer consultation", PERMISSION_INVITE_TO_TALKBACKCONSULTATION: "Send invitations", }) section_sort_order = tuple() def __init__(self, id, contributor): """ """ self.id = id self.contributor = contributor NyContainer.__dict__['__init__'](self) NyProperties.__dict__['__init__'](self) self.invitations = InvitationsContainer('invitations') self.submitted = 1 def set_allow_reviewer_invites(self, allow): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) if allow and 'Reviewer' not in roles: new_roles = roles + ['Reviewer'] elif not allow and 'Reviewer' in roles: new_roles = tuple(role for role in roles if role != 'Reviewer') else: new_roles = roles if new_roles: setattr(self, perm, new_roles) else: if hasattr(self, perm): delattr(self, perm) def get_allow_reviewer_invites(self): perm = '_Naaya___Invite_to_TalkBack_Consultation_Permission' roles = getattr(self, perm, []) return ('Reviewer' in roles) allow_reviewer_invites = property(get_allow_reviewer_invites, set_allow_reviewer_invites) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'saveProperties') def saveProperties(self, REQUEST=None, **kwargs): """ """ if not self.checkPermissionEditObject(): raise EXCEPTION_NOTAUTHORIZED(EXCEPTION_NOTAUTHORIZED_MSG) if REQUEST is not None: schema_raw_data = dict(REQUEST.form) else: schema_raw_data = kwargs _lang = schema_raw_data.pop('_lang', schema_raw_data.pop('lang', None)) _releasedate = self.process_releasedate( schema_raw_data.pop('releasedate', ''), self.releasedate) form_errors = self.process_submitted_form( schema_raw_data, _lang, _override_releasedate=_releasedate) if not form_errors: self._p_changed = True self.recatalogNyObject(self) # log date contributor = self.REQUEST.AUTHENTICATED_USER.getUserName() auth_tool = self.getAuthenticationTool() auth_tool.changeLastPost(contributor) notify(NyContentObjectEditEvent(self, contributor)) if REQUEST: self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: if REQUEST is not None: self._prepare_error_response(REQUEST, form_errors, schema_raw_data) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), _lang)) else: raise ValueError(form_errors.popitem()[1]) # pick an error security.declareProtected(view, 'get_consultation') def get_consultation(self): return self security.declareProtected(view, 'list_sections') def list_sections(self): """ """ metatypes = [METATYPE_TALKBACKCONSULTATION_SECTION] sections = dict(self.objectItems(metatypes)) output = [] for section_id in self.section_sort_order: if section_id in sections: output.append(sections.pop(section_id)) output.extend(sorted(sections.values(), key=operator.attrgetter('title'))) return output security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'save_sort_order') def save_sort_order(self, sort_section_id, REQUEST=None): """ save the sort order of sections """ self.section_sort_order = tuple(sort_section_id) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) _comments_atom = NaayaPageTemplateFile('zpt/comments_atom', globals(), 'tbconsultation_comments_atom') security.declareProtected(view, 'comments_atom') def comments_atom(self, REQUEST=None, days=2): """ ATOM feed with consultation comments """ if isinstance(days, basestring): try: days = int(days) except: days = 2 cutoff = DateTime() - days comments_list = [] for section in self.list_sections(): for paragraph in section.get_paragraphs(): for comment in paragraph.get_comments(): if comment.comment_date < cutoff: continue comments_list.append(comment) comments_list.sort(key=operator.attrgetter('comment_date'), reverse=True) if comments_list: feed_updated = comments_list[0].comment_date else: feed_updated = DateTime() def atom_date_format(date): d = DT2dt(date).strftime('%Y-%m-%dT%H:%M:%S%z') return d[:-2] + ':' + d[-2:] if REQUEST is not None: REQUEST.RESPONSE.setHeader('Content-Type', 'application/atom+xml') return self._comments_atom(comments_list=comments_list, atom_date_format=atom_date_format, feed_updated=feed_updated) security.declareProtected(view, 'get_start_date') def get_start_date(self): """ Returns the start date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.start_date) security.declareProtected(view, 'get_end_date') def get_end_date(self): """ Returns the end date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.end_date) security.declareProtected(view, 'get_days_left') def get_days_left(self): """ Returns the remaining days for the consultation or the number of days before it starts """ today = self.utGetTodayDate().earliestTime() if not self.start_date or not self.end_date: return (1, 0) after_end_date = self.end_date + 1 if self.start_date.lessThanEqualTo(today): return (1, int(str(after_end_date - today).split('.')[0])) else: return (0, int(str(self.start_date - today).split('.')[0])) security.declareProtected(view_management_screens, 'manage_options') def manage_options(self): """ """ l_options = (NyContainer.manage_options[0],) l_options += ({'label': 'View', 'action': 'index_html'},) + \ NyContainer.manage_options[3:8] return l_options security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'delete_sections') def delete_sections(self, del_section_id, REQUEST=None): """ remove the specified sections """ self.manage_delObjects(list(del_section_id)) self.setSessionInfoTrans('Removed ${count} sections.', count=str(len(del_section_id))) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) def get_user_name(self): # first, check if we have an invite key invitation = self.invitations.get_current_invitation(self.REQUEST) if invitation is not None: return invitation.name # no invite key; look for current Zope user auth_tool = self.getAuthenticationTool() userid = auth_tool.get_current_userid() if userid is None: # anonymous user return None name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def get_user_name_or_userid(self, userid=None): if userid is None: return self.get_user_name() auth_tool = self.getAuthenticationTool() name = auth_tool.name_from_userid(userid) if name == '': name = userid return name def checkTalkBackConsultationUser(self): """ Checks if the user is logged in and has reviewer rights: 0 if user is anonymous, 1 if user has reviewer role 2 if user doesn't have reviewer role """ review_check = self.checkPermissionReviewTalkBackConsultation() if self.isAnonymousUser(): return 0 elif review_check: return 1 elif not review_check: return 2 security.declareProtected(view, 'check_cannot_comment') def check_cannot_comment(self): """ """ if not self.checkPermissionReviewTalkBackConsultation(): if self.isAnonymousUser(): return 'not-logged-in' else: return 'no-permission' if self.get_days_left()[1] <= 0 and not ( self.checkPermissionManageTalkBackConsultation() or self.checkPermissionReviewTalkBackConsultationAfterDeadline()): return 'deadline-reached' security.declareProtected( PERMISSION_COMMENTS_ADD, 'log_in_authenticated') def log_in_authenticated(self, REQUEST=None): """ Log in user and redirect to TalkBack Consultation index """ if REQUEST is not None: self.REQUEST.RESPONSE.redirect(self.absolute_url()) # permissions def checkPermissionReviewTalkBackConsultation(self): """ Check for reviewing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_REVIEW_TALKBACKCONSULTATION) security.declareProtected( PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE, 'review_after_deadline') def review_after_deadline(self): """ Dummy function to register the permission. """ raise NotImplementedError def checkPermissionReviewTalkBackConsultationAfterDeadline(self): """ Check for reviewing the TalkBack Consultation after the deadline has passed. """ return self.checkPermission( PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE) def checkPermissionManageTalkBackConsultation(self): """ Check for managing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_MANAGE_TALKBACKCONSULTATION) def checkPermissionInviteToTalkBackConsultation(self): """ Check for inviting others to the TalkBack Consultation. """ return self.checkPermission(PERMISSION_INVITE_TO_TALKBACKCONSULTATION) def own_comments(self): """ Check if the current user has any comments on the consultation """ return [comment for comment in self.admin_comments._all_comments() if comment['comment'].contributor == self.REQUEST.AUTHENTICATED_USER.getUserName()] security.declareProtected(view, 'custom_editor') def custom_editor(self, editor_tool, lang, dom_id): extra_options = { 'content_css': self.absolute_url() + '/misc_/NaayaContent/tb-editor.css', 'theme_advanced_buttons1': 'bold,italic,underline,strikethrough,sub,sup,forecolor,' 'backcolor,removeformat,separator,' 'bullist,numlist,separator,' 'justifyleft,justifycenter,justifyright,justifyfull,separator,' 'link,unlink,hr,image,separator,' 'pastetext,pasteword,cleanup,code,help', 'theme_advanced_buttons2': '', } return editor_tool.render(dom_id, lang, image_support=True, extra_options=extra_options) security.declareProtected(view, 'get_files') def get_files(self): """ Get a list of all files attached to the consultation""" return [ob for ob in self.objectValues('Naaya Blob File')] security.declareProtected(view, 'findUsers') def findUsers(self, search_param, search_term): """ """ if len(search_term) == 0: return [] return json.dumps(findUsers(self.getSite(), search_param, search_term)) addSection = addSection # zmi pages security.declareProtected(view_management_screens, 'manage_edit_html') manage_edit_html = PageTemplateFile('zpt/talkbackconsultation_manage_edit', globals()) # site pages security.declareProtected(view, 'index_html') index_html = NaayaPageTemplateFile('zpt/talkbackconsultation_index', globals(), 'tbconsultation_index') # standard_template_macro, header and footer templates are proxied # since invited reviewers have "View" permission only in this folder; # if the consultation is restricted, they would not be able to see # consultation pages. def standard_html_header(self, *args, **kwargs): return self.aq_parent.standard_html_header(*args, **kwargs) def standard_html_footer(self, *args, **kwargs): return self.aq_parent.standard_html_footer(*args, **kwargs) def standard_template_macro(self, *args, **kwargs): return self.aq_parent.standard_template_macro(*args, **kwargs) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'edit_html') edit_html = NaayaPageTemplateFile('zpt/talkbackconsultation_edit', globals(), 'tbconsultation_edit') security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'section_add_html') section_add_html = addSection_html __allow_groups__ = InvitationUsersTool() _View_Permission = ['InvitedReviewer'] _Naaya___Review_TalkBack_Consultation_Permission = ['InvitedReviewer'] __ac_roles__ = ['InvitedReviewer'] admin_comments = CommentsAdmin('admin_comments')
class NyTalkBackConsultation( Implicit, NyContentData, NyContentType, NyAttributes, NyProperties, NyRoleManager, NyContainer, NyNonCheckControl, utils, ): """ """ meta_type = METATYPE_TALKBACKCONSULTATION meta_label = LABEL_OBJECT meta_types = [ { "name": METATYPE_TALKBACKCONSULTATION_SECTION, "action": "addSection", "permission": PERMISSION_MANAGE_TALKBACKCONSULTATION, } ] icon = "misc_/NaayaContent/NyTalkBackConsultation.gif" icon_marked = "misc_/NaayaContent/NyTalkBackConsultation_marked.gif" security = ClassSecurityInfo() edit_access = NyAccess( "edit_access", { PERMISSION_REVIEW_TALKBACKCONSULTATION: "Submit comments", PERMISSION_MANAGE_TALKBACKCONSULTATION: "Administer consultation", PERMISSION_INVITE_TO_TALKBACKCONSULTATION: "Send invitations", }, ) section_sort_order = tuple() def __init__(self, id, contributor): """ """ self.id = id self.contributor = contributor NyContainer.__dict__["__init__"](self) NyProperties.__dict__["__init__"](self) self.invitations = InvitationsContainer("invitations") self.submitted = 1 def set_allow_reviewer_invites(self, allow): perm = "_Naaya___Invite_to_TalkBack_Consultation_Permission" roles = getattr(self, perm, []) if allow and "Reviewer" not in roles: roles.append("Reviewer") elif not allow and "Reviewer" in roles: roles.remove("Reviewer") if roles: setattr(self, perm, roles) else: if hasattr(self, perm): delattr(self, perm) def get_allow_reviewer_invites(self): perm = "_Naaya___Invite_to_TalkBack_Consultation_Permission" roles = getattr(self, perm, []) return "Reviewer" in roles allow_reviewer_invites = property(get_allow_reviewer_invites, set_allow_reviewer_invites) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, "saveProperties") def saveProperties(self, REQUEST=None, **kwargs): """ """ if not self.checkPermissionEditObject(): raise EXCEPTION_NOTAUTHORIZED, EXCEPTION_NOTAUTHORIZED_MSG if REQUEST is not None: schema_raw_data = dict(REQUEST.form) else: schema_raw_data = kwargs _lang = schema_raw_data.pop("_lang", schema_raw_data.pop("lang", None)) _releasedate = self.process_releasedate(schema_raw_data.pop("releasedate", ""), self.releasedate) form_errors = self.process_submitted_form(schema_raw_data, _lang, _override_releasedate=_releasedate) if not form_errors: self._p_changed = True self.recatalogNyObject(self) # log date contributor = self.REQUEST.AUTHENTICATED_USER.getUserName() auth_tool = self.getAuthenticationTool() auth_tool.changeLastPost(contributor) notify(NyContentObjectEditEvent(self, contributor)) if REQUEST: self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) REQUEST.RESPONSE.redirect("%s/edit_html?lang=%s" % (self.absolute_url(), _lang)) else: if REQUEST is not None: self._prepare_error_response(REQUEST, form_errors, schema_raw_data) REQUEST.RESPONSE.redirect("%s/edit_html?lang=%s" % (self.absolute_url(), _lang)) else: raise ValueError(form_errors.popitem()[1]) # pick a random error security.declareProtected(view, "get_consultation") def get_consultation(self): return self security.declareProtected(view, "list_sections") def list_sections(self): """ """ metatypes = [METATYPE_TALKBACKCONSULTATION_SECTION] sections = dict(self.objectItems(metatypes)) output = [] for section_id in self.section_sort_order: if section_id in sections: output.append(sections.pop(section_id)) output.extend(sorted(sections.values(), key=operator.attrgetter("title"))) return output security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, "save_sort_order") def save_sort_order(self, sort_section_id, REQUEST=None): """ save the sort order of sections """ self.section_sort_order = tuple(sort_section_id) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) _comments_atom = NaayaPageTemplateFile("zpt/comments_atom", globals(), "tbconsultation_comments_atom") security.declareProtected(view, "comments_atom") def comments_atom(self, REQUEST=None, days=2): """ ATOM feed with consultation comments """ if isinstance(days, basestring): try: days = int(days) except: days = 2 cutoff = DateTime() - days comments_list = [] for section in self.list_sections(): for paragraph in section.get_paragraphs(): for comment in paragraph.get_comments(): if comment.comment_date < cutoff: continue comments_list.append(comment) comments_list.sort(key=operator.attrgetter("comment_date"), reverse=True) if comments_list: feed_updated = comments_list[0].comment_date else: feed_updated = DateTime() def atom_date_format(date): d = DT2dt(date).strftime("%Y-%m-%dT%H:%M:%S%z") return d[:-2] + ":" + d[-2:] if REQUEST is not None: REQUEST.RESPONSE.setHeader("Content-Type", "application/atom+xml") return self._comments_atom( comments_list=comments_list, atom_date_format=atom_date_format, feed_updated=feed_updated ) security.declareProtected(view, "get_start_date") def get_start_date(self): """ Returns the start date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.start_date) security.declareProtected(view, "get_end_date") def get_end_date(self): """ Returns the end date in dd/mm/yyyy string format. """ return self.utConvertDateTimeObjToString(self.end_date) security.declareProtected(view, "get_days_left") def get_days_left(self): """ Returns the remaining days for the consultation or the number of days before it starts """ today = self.utGetTodayDate().earliestTime() if not self.start_date or not self.end_date: return (1, 0) after_end_date = self.end_date + 1 if self.start_date.lessThanEqualTo(today): return (1, int(str(after_end_date - today).split(".")[0])) else: return (0, int(str(self.start_date - today).split(".")[0])) security.declareProtected(view_management_screens, "manage_options") def manage_options(self): """ """ l_options = (NyContainer.manage_options[0],) l_options += ({"label": "View", "action": "index_html"},) + NyContainer.manage_options[3:8] return l_options security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, "delete_sections") def delete_sections(self, del_section_id, REQUEST=None): """ remove the specified sections """ self.manage_delObjects(list(del_section_id)) self.setSessionInfoTrans("Removed ${count} sections.", count=str(len(del_section_id))) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url()) def get_user_name(self): # first, check if we have an invite key invitation = self.invitations.get_current_invitation(self.REQUEST) if invitation is not None: return invitation.name # no invite key; look for current Zope user auth_tool = self.getAuthenticationTool() userid = auth_tool.get_current_userid() if userid is None: # anonymous user return None name = auth_tool.name_from_userid(userid) if name == "": name = userid return name def get_user_name_or_userid(self, userid=None): if userid is None: return self.get_user_name() auth_tool = self.getAuthenticationTool() name = auth_tool.name_from_userid(userid) if name == "": name = userid return name def checkTalkBackConsultationUser(self): """ Checks if the user is logged in and has reviewer rights: 0 if user is anonymous, 1 if user has reviewer role 2 if user doesn't have reviewer role """ review_check = self.checkPermissionReviewTalkBackConsultation() if self.isAnonymousUser(): return 0 elif review_check: return 1 elif not review_check: return 2 security.declareProtected(view, "check_cannot_comment") def check_cannot_comment(self): """ """ if not self.checkPermissionReviewTalkBackConsultation(): if self.isAnonymousUser(): return "not-logged-in" else: return "no-permission" if self.get_days_left()[1] <= 0 and not ( self.checkPermissionManageTalkBackConsultation() or self.checkPermissionReviewTalkBackConsultationAfterDeadline() ): return "deadline-reached" security.declareProtected(PERMISSION_COMMENTS_ADD, "log_in_authenticated") def log_in_authenticated(self, REQUEST=None): """ Log in user and redirect to TalkBack Consultation index """ if REQUEST is not None: self.REQUEST.RESPONSE.redirect(self.absolute_url()) # permissions def checkPermissionReviewTalkBackConsultation(self): """ Check for reviewing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_REVIEW_TALKBACKCONSULTATION) security.declareProtected(PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE, "review_after_deadline") def review_after_deadline(self): """ Dummy function to register the permission. """ raise NotImplementedError def checkPermissionReviewTalkBackConsultationAfterDeadline(self): """ Check for reviewing the TalkBack Consultation after the deadline has passed. """ return self.checkPermission(PERMISSION_REVIEW_TALKBACKCONSULTATION_AFTER_DEADLINE) def checkPermissionManageTalkBackConsultation(self): """ Check for managing the TalkBack Consultation. """ return self.checkPermission(PERMISSION_MANAGE_TALKBACKCONSULTATION) def checkPermissionInviteToTalkBackConsultation(self): """ Check for inviting others to the TalkBack Consultation. """ return self.checkPermission(PERMISSION_INVITE_TO_TALKBACKCONSULTATION) security.declareProtected(view, "custom_editor") def custom_editor(self, editor_tool, lang, dom_id): extra_options = { "content_css": self.absolute_url() + "/misc_/NaayaContent/tb-editor.css", "theme_advanced_buttons1": "bold,italic,underline,strikethrough,sub,sup,forecolor," "backcolor,removeformat,separator," "bullist,numlist,separator," "justifyleft,justifycenter,justifyright,justifyfull,separator," "link,unlink,hr,image,separator," "pastetext,pasteword,cleanup,code,help", "theme_advanced_buttons2": "", } return editor_tool.render(dom_id, lang, image_support=True, extra_options=extra_options) addSection = addSection # zmi pages security.declareProtected(view_management_screens, "manage_edit_html") manage_edit_html = PageTemplateFile("zpt/talkbackconsultation_manage_edit", globals()) # site pages security.declareProtected(view, "index_html") index_html = NaayaPageTemplateFile("zpt/talkbackconsultation_index", globals(), "tbconsultation_index") # standard_template_macro, header and footer templates are proxied # since invited reviewers have "View" permission only in this folder; # if the consultation is restricted, they would not be able to see # consultation pages. def standard_html_header(self, *args, **kwargs): return self.aq_parent.standard_html_header(*args, **kwargs) def standard_html_footer(self, *args, **kwargs): return self.aq_parent.standard_html_footer(*args, **kwargs) def standard_template_macro(self, *args, **kwargs): return self.aq_parent.standard_template_macro(*args, **kwargs) security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, "edit_html") edit_html = NaayaPageTemplateFile("zpt/talkbackconsultation_edit", globals(), "tbconsultation_edit") security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, "section_add_html") section_add_html = addSection_html __allow_groups__ = InvitationUsersTool() _View_Permission = ["InvitedReviewer"] _Naaya___Review_TalkBack_Consultation_Permission = ["InvitedReviewer"] __ac_roles__ = ["InvitedReviewer"] admin_comments = CommentsAdmin("admin_comments")