def afterSetUp(self): self.my_tmpl = NaayaPageTemplateFile('the_template', globals(), 'my_tmpl') self.second_tmpl = NaayaPageTemplateFile('the_template', globals(), 'second_tmpl') NyDocument.my_tmpl = self.my_tmpl NyDocument.second_tmpl = self.second_tmpl
def afterSetUp(self): self.my_tmpl = NaayaPageTemplateFile('the_template', globals(), 'my_tmpl') NyDocument.my_tmpl = self.my_tmpl self.second_tmpl = NaayaPageTemplateFile('the_template', globals(), 'second_tmpl') NyDocument.second_tmpl = self.second_tmpl
class NaayaTemplateTestCase(NaayaTestCase.NaayaTestCase): def afterSetUp(self): self.my_tmpl = NaayaPageTemplateFile('the_template', globals(), 'my_tmpl') NyDocument.my_tmpl = self.my_tmpl def beforeTearDown(self): if hasattr(NyDocument, 'my_tmpl'): del NyDocument.my_tmpl def test_default(self): output = self.my_tmpl.__of__(self.portal)(a="26") self.assertTrue('[physical path: /portal]' in output) self.assertTrue('[option a: 26]' in output) output = self.portal.info.contact.my_tmpl(a="13") self.assertTrue('[physical path: /portal/info/contact]' in output) self.assertTrue('[option a: 13]' in output) def test_customize(self): forms_tool = self.portal.portal_forms ids = [f['id'] for f in forms_tool.listDefaultForms()] self.assertTrue('my_tmpl' in ids) my_tmpl_aq = forms_tool.getForm('my_tmpl') self.assertTrue(my_tmpl_aq.aq_self is NyDocument.my_tmpl) self.assertTrue(my_tmpl_aq.aq_parent is forms_tool) forms_tool.manage_customizeForm('my_tmpl') forms_tool.my_tmpl.pt_edit(text='new content', content_type='text/html') self.assertEqual(self.portal.info.contact.my_tmpl().strip(), 'new content')
class LogEntry(SimpleItem, UtilsManager): """LogEntry class""" meta_type = 'LogEntry' icon = 'misc_/NaayaLinkChecker/logentry' manage_options = ({ 'label': 'View', 'action': 'index_html', }, ) security = ClassSecurityInfo() def __init__(self, id, user, date_create, url_list): """Constructor""" self.id = id self.title = 'Log Entry at %s' % self.umFormatDateTimeToString( date_create) self.user = user self.date_create = date_create self.url_list = url_list UtilsManager.__dict__['__init__'](self) security.declareProtected(view, 'index_html') index_html = NaayaPageTemplateFile('zpt/LogEntry_index', globals(), 'linkchecker_log_index')
class ExpertsLister(Implicit, Item): """ Plug into the catalog to retrieve the list of experts """ def __init__(self, id): self.id = id _index_template = NaayaPageTemplateFile('zpt/experts_list', globals(), 'expert') def index_html(self, REQUEST): """ Render the list of organisations recorded for this site. """ return self._index_template(REQUEST, experts=[1, 2, 3]) def items_in_topic(self, topic=None, filter_name=None, objects=False): filters = {'meta_type': 'Naaya Expert', 'approved': True} if topic is not None: filters['topics'] = topic if filter_name is not None: filters['title'] = '*%s*' % filter_name catalog = self.getCatalogTool() if objects: items = [catalog.getobject(ob.data_record_id_) for ob in catalog.search(filters)] return sorted(items, key=lambda ob: ob.surname.strip().lower()) else: return catalog.search(filters)
class ObjectListingPortlet(object): title = 'List contained objects' def __init__(self, site): self.site = site def __call__(self, context, position): return self.template.__of__(context)() template = NaayaPageTemplateFile('zpt/listing_portlet', globals(), 'naaya.core.folder.listing_portlet')
class InvitationsContainer(SimpleItem): security = ClassSecurityInfo() title = "Consultation invitations" def __init__(self, id): super(SimpleItem, self).__init__(id) self.id = id self._invites = OOBTree() _create_html = NaayaPageTemplateFile('zpt/invitations_create', globals(), 'tbconsultation_invitations_create') security.declareProtected(PERMISSION_INVITE_TO_TALKBACKCONSULTATION, 'create') def create(self, REQUEST): """ Create an invitation, send e-mail """ keys = ('name', 'email', 'organization', 'notes', 'message') formerrors = {} previews = [] if REQUEST.REQUEST_METHOD == 'POST': do_preview = (REQUEST.form.get('do', '') == 'Preview') inviter_userid, inviter_name = self._get_inviter_info() formdata = dict((key, REQUEST.form.get(key, '')) for key in keys) kwargs = dict(formdata, web_form=True, inviter_userid=inviter_userid, inviter_name=inviter_name) try: if do_preview: preview = self._send_invitation(preview=True, **kwargs) preview['preview_attribution'] = '%s (invited by %s)' % \ (formdata['name'], inviter_name) previews.append(preview) else: self._send_invitation(**kwargs) self.setSessionInfoTrans( 'Invitation for ${name} ' 'has been sent.', name=formdata['name']) return REQUEST.RESPONSE.redirect(self.absolute_url() + '/create') except FormError, e: self.setSessionErrorsTrans('The form contains errors. Please ' 'correct them and try again.') formerrors = dict(e.errors) else:
def register_naaya_content(_context, factory, **kwargs): """ Register a naaya content type. This is called for each content type using it's config dictionary. This will validate the config of the content type, perform a few sanity checks, declare the permissions for the content type and register the templates. """ assert factory, "No factory provided" assert callable(factory), "Factory must be callable" config = factory() assert 'meta_type' in config, "Config must contain at least a meta_type" _contents = NaayaContent._contents _misc = NaayaContent._misc _constants = NaayaContent._constants _contents[config['meta_type']] = config _misc.update(config.get('_misc', {})) # register _misc into Zope if not Application.misc_.__dict__.has_key('NaayaContent'): Application.misc_.__dict__['NaayaContent'] = Misc_('NaayaContent', _misc) else: Application.misc_.__dict__['NaayaContent'].__dict__['_d'].update(_misc) # used by site_header _constants['METATYPE_FOLDER'] = 'Naaya Folder' security = ClassSecurityInfo() NyFolderBase.security = security # declare permissions for object constructors if (config['meta_type'] != 'Naaya Folder' and 'folder_constructors' in config): for folder_property, object_constructor in config['folder_constructors']: setattr(NyFolderBase, folder_property, object_constructor) NyFolderBase.security.declareProtected(config['permission'], folder_property) # register forms for form in config.get('forms', []): NaayaPageTemplateFile(os.path.join(config['package_path'], 'zpt', form), globals(), form) InitializeClass(NyFolderBase) # log success zLOG.LOG('naaya.content', zLOG.DEBUG, 'Pluggable content type "%s" registered' % config['meta_type'])
class CHMTermsTagCloudPortlet(object): interface.implements(INyPortlet) component.adapts(ICHMSite) title = 'CHM Terms' def __init__(self, site): self.site = site def chm_terms_frequency(self): attribute = 'chm_terms' separator = '|' lang = self.site.gl_get_selected_language() objects = self.site.getCatalogedObjectsCheckView(meta_type=[ 'Naaya Folder', 'Naaya Photo Folder', 'Naaya Photo Gallery', 'Naaya Contact', 'Naaya Survey', 'Naaya Educational Product', 'Naaya News', 'Naaya Story', 'Naaya File', 'Naaya URL', 'Naaya Extended File', 'Naaya Document', 'Naaya Event', 'Naaya Media File', 'Naaya Pointer', 'Naaya Blob File', 'Naaya Localized Blob File', 'Naaya GeoPoint' ]) ret = {} for ob in objects: if ob.hasLocalProperty(attribute): value = ob.getLocalAttribute(attribute, lang) elif hasattr(ob, attribute): value = getattr(ob, attribute) else: continue if not value or not isinstance(value, basestring): continue for raw_term in value.split(separator): term = raw_term.strip() ret.setdefault(term, 0) ret[term] += 1 return ret def __call__(self, context, position): macro = self.site.getPortletsTool()._get_macro(position) return self.template.__of__(context)(macro=macro, tags=self.chm_terms_frequency()) template = NaayaPageTemplateFile( 'skel/portlets/portlet_chmterms_tagcloud', globals(), 'Products.CHM2.portlets.portlet_chmterms_tagcloud')
class MainSectionsPortlet(object): interface.implements(INyPortlet) component.adapts(INySite) title = 'Main sections' def __init__(self, site): self.site = site def __call__(self, context, position): macro = self.site.getPortletsTool()._get_macro(position) return self.template.__of__(context)(macro=macro) template = NaayaPageTemplateFile('zpt/mainsections_portlet', globals(), 'naaya.core.mainsections_portlet')
class ObjectListingPortlet(object): implements(INyPortlet) adapts(IEWSite) title = 'List contained objects' def __init__(self, site): self.site = site def __call__(self, context, position): return self.template.__of__(context)() template = NaayaPageTemplateFile( 'zpt/listing_portlet', globals(), 'naaya.envirowindows.folder.listing_portlet')
class AdministrationPortlet(object): interface.implements(INyPortlet) component.adapts(ICHMBESite) title = 'Administration' def __init__(self, site): self.site = site def __call__(self, context, position): macro = self.site.getPortletsTool()._get_macro(position) return self.template.__of__(context)(macro=macro) template = NaayaPageTemplateFile('skel/portlets/portlet_administration', globals(), 'Products.CHM2BE.portlets.portlet_administration')
class NavigationPortlet(object): implements(INyPortlet) adapts(INySite) title = 'Navigation' def __init__(self, site): self.site = site def __call__(self, context, position): macro = self.site.getPortletsTool()._get_macro(position) return self.template.__of__(context)(macro=macro) template = NaayaPageTemplateFile('zpt/navigation_portlet', globals(), 'naaya.groupware.navigation_portlet')
class OrganisationLister(Implicit, Item): _index_template = NaayaPageTemplateFile('zpt/organisations_list', globals(), 'organisation') """ Plug into the catalog to retrieve the list of organisations Render the list of organisations recorded for this site. """ def __init__(self, id): self.id = id def index_html(self, REQUEST): """ Index page """ return self._index_template(REQUEST) def items_in_topic(self, topic=None, filter_name=None, objects=False): filters = {'meta_type': METATYPE_OBJECT, 'approved': True} if topic is not None: default_lang = self.gl_get_default_language() default_lang_name = self.gl_get_language_name(default_lang) glossary = self.getSite().chm_terms try: item_brains = glossary.getObjectByCode(topic) if item_brains: #if the topic is a glossary element, the list is not empty #this list should contain only one object if the id is unique glossary_item = item_brains[0].getObject() else: #the topic is a glossary folder glossary_item = getattr(glossary, topic) topic_title = glossary_item.get_translation_by_language( default_lang_name) filters['topics'] = topic_title except AttributeError: #an invalid topic was passed return None if filter_name is not None: filters['title'] = '*%s*' % filter_name catalog = self.getCatalogTool() if objects: items = [ catalog.getobject(ob.data_record_id_) for ob in catalog.search(filters) ] return sorted(items, key=lambda ob: ob.title.strip().lower()) else: return catalog.search(filters)
class AdministrationPortlet(object): implements(INyPortlet) adapts(IGWSite) title = 'Administration' def __init__(self, site): self.site = site def __call__(self, context, position): macro = self.site.getPortletsTool()._get_macro(position) return self.template.__of__(context)(macro=macro) template = NaayaPageTemplateFile( 'skel/portlets/portlet_administration', globals(), 'naaya.groupware.portlets.portlet_administration')
def register_templates_in_directory(templates_path, bundle_name): """ Register all templates found in a folder. """ from Products.NaayaCore.FormsTool.NaayaTemplate import NaayaPageTemplateFile if not os.path.isdir(templates_path): return count = 0 for filename in os.listdir(templates_path): fileroot, ext = os.path.splitext(filename) if ext in ('.zpt', '.pt', ): tmpl_path = os.path.join(templates_path, filename) NaayaPageTemplateFile(tmpl_path, globals(), fileroot, bundle_name) count += 1 if count: log.debug("Loaded %d templates from %r into bundle %r", count, templates_path, bundle_name)
class NotificationsPortlet(object): title = 'Subscribe to notifications' def __init__(self, site): self.site = site def __call__(self, context, position): notif_tool = self.site.getNotificationTool() #The portlet should only be rendered if at least one notification type #is activated, of if the user has administrative rights, so he should #be able to subscribe to administrative notifications if not list(notif_tool.available_notif_types()) and not \ self.site.checkPermissionPublishObjects(): return '' macro = self.site.getPortletsTool()._get_macro(position) tmpl = self.template.__of__(context) location = path_in_site(context) if context == notif_tool: location = '' return tmpl(macro=macro, notif_tool=notif_tool, location=location) template = NaayaPageTemplateFile('zpt/portlet', globals(), 'naaya.core.notifications.notifications_portlet')
from events import ItemTranslationChanged # constants LABEL_OBJECT = 'Glossary element' class ElementBasic: """ define the basic properties for NyGlossaryElement """ def __init__(self, title, source, contributor): """ constructor """ self.title = title self.source = source self.contributor = contributor manage_addGlossaryElement_html = NaayaPageTemplateFile( 'zpt/NaayaGlossaryElement/add', globals(), 'glossary_element_add') def manage_addGlossaryElement(self, id='', title='', source='', subjects=[], contributor='', approved=1, REQUEST=None): """ adds a new NyGlossaryElement object """ ob = NyGlossaryElement(id, title, source, subjects, contributor, approved) self._setObject(id, ob) element_obj = self._getOb(id) element_obj.subjects = self.get_subject_by_codes(subjects)
class MegaSurvey(SurveyQuestionnaire, BaseSurveyTemplate): """ """ meta_type = 'Naaya Mega Survey' meta_label = 'Survey' _constructors = (manage_addMegaSurvey, ) security = ClassSecurityInfo() edit_access = NyAccess( 'edit_access', { PERMISSION_ADD_ANSWER: "Submit answer", PERMISSION_ADD_REPORT: "Create report", PERMISSION_ADD_ATTACHMENT: "Attach file", PERMISSION_VIEW_ANSWERS: "View answers", PERMISSION_EDIT_ANSWERS: "Edit answers", PERMISSION_VIEW_REPORTS: "View reports", }) def __init__(self, id, **kwargs): """ """ # BaseSurveyTemplate.__init__(self, id, **kwargs) SurveyQuestionnaire.__init__(self, id, None, **kwargs) self.contributor = kwargs.get('contributor') self.approved = 1 def can_be_seen(self): """ Indicates if the current user has access to the current folder. """ return self.checkPermission(view) def all_meta_types(self, interfaces=None): """What can you put inside me?""" return BaseSurveyTemplate.all_meta_types(self, interfaces) def getSurveyTemplate(self): """ """ return self security.declareProtected(view, 'download') def download(self, REQUEST=None, RESPONSE=None): """returns all the answers in a csv file""" def stringify(value): if not isinstance(value, basestring): value = unicode(value) if isinstance(value, str): return unicode(value, 'utf-8') return value def all_stringify(row): return [stringify(value) for value in row] answers = self.getAnswers() widgets = self.getSortedWidgets() header = ['Respondent'] for widget in widgets: header += [widget.title_or_id()] if widget.meta_type == 'Naaya Radio Matrix Widget': header += widget.rows rows = [answer.answer_values() for answer in answers] rows = [all_stringify(item) for item in rows] file_type = REQUEST.get('file_type', 'CSV') if file_type == 'CSV': RESPONSE.setHeader('Content-Type', 'text/csv') RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s.csv' % self.id) return generate_csv(header, rows) if file_type == 'Excel' and self.rstk.we_provide('Excel export'): RESPONSE.setHeader('Content-Type', 'application/vnd.ms-excel') RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s.xls' % self.id) return generate_excel(header, rows) else: raise ValueError('unknown file format %r' % file_type) # # Site pages # security.declareProtected(view, 'index_html') def index_html(self): """ """ if (not self.checkPermissionSkipCaptcha() and not self.recaptcha_is_present()): raise ValueError("Invalid recaptcha keys") return self._index_html() _index_html = NaayaPageTemplateFile('zpt/megasurvey_index', globals(), 'NaayaSurvey.megasurvey_index') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_html') edit_html = NaayaPageTemplateFile('zpt/megasurvey_edit', globals(), 'NaayaSurvey.megasurvey_edit') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_attachments_html') edit_attachments_html = NaayaPageTemplateFile( 'zpt/megasurvey_edit_attachments', globals(), 'NaayaSurvey.megasurvey_edit_attachments') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_questions_html') edit_questions_html = NaayaPageTemplateFile( 'zpt/megasurvey_edit_questions', globals(), 'NaayaSurvey.megasurvey_edit_questions') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'edit_reports_html') edit_reports_html = NaayaPageTemplateFile( 'zpt/megasurvey_edit_reports', globals(), 'NaayaSurvey.megasurvey_edit_reports') # # change the security of the inherited methods # security.declareProtected(PERMISSION_EDIT_OBJECTS, 'saveProperties') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'addWidget') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'deleteItems') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'setSortOrder') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'addReport') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'generateFullReport') security.declareProtected(PERMISSION_EDIT_OBJECTS, 'addAttachment') # static files css_survey_common = DTMLFile('www/survey_common.css', globals()) css_survey_print = DTMLFile('www/survey_print.css', globals()) fancy_checkmark = ImageFile('www/fancy_checkmark.gif', globals()) survey_js = ImageFile('www/survey.js', globals()) security.declareProtected(PERMISSION_EDIT_ANSWERS, 'bogus') def bogus(self): """ Needed in Naaya Access. It is mandatory that a permission must be declared so it can be used in Naaya Access. This should be removed once this issue is solved """ pass security.declarePublic('display_admin_warning') def display_admin_warning(self): return self.checkPermissionPublishObjects()\ and self.anonymous_has_access()\ and not self.recaptcha_is_present()\ and not self.anonymous_skips_captcha() security.declarePublic('anonymous_has_access') def anonymous_has_access(self): return 'Anonymous' in self.edit_access.getPermissionMapping( )['Naaya - Add Naaya Survey Answer'] security.declarePublic('anonymous_skips_captcha') def anonymous_skips_captcha(self): permission = 'Naaya - Skip Captcha' permission_object = Permission(permission, (), self) return 'Anonymous' in permission_object.getRoles() security.declarePrivate('dont_inherit_view_permission') def dont_inherit_view_permission(self): permission = Permission(view, (), self) roles = permission.getRoles() roles = tuple(set(roles) | set(['Manager', 'Administrator', 'Owner'])) permission.setRoles(roles) security.declarePrivate('inherit_view_permission') def inherit_view_permission(self): permission = Permission(view, (), self) roles = permission.getRoles() roles = list(roles) permission.setRoles(roles)
from Products.NaayaBase.NyAccess import NyAccess from Products.NaayaCore.FormsTool.NaayaTemplate import NaayaPageTemplateFile from Products.NaayaCore.managers.import_export import generate_csv from Products.NaayaCore.managers.import_export import generate_excel from Products.NaayaWidgets.constants import PERMISSION_ADD_WIDGETS from BaseSurveyTemplate import BaseSurveyTemplate from SurveyQuestionnaire import SurveyQuestionnaire from permissions import (PERMISSION_ADD_MEGASURVEY, PERMISSION_ADD_ANSWER, PERMISSION_ADD_REPORT, PERMISSION_ADD_ATTACHMENT, PERMISSION_VIEW_ANSWERS, PERMISSION_EDIT_ANSWERS, PERMISSION_VIEW_REPORTS) log = logging.getLogger(__name__) megasurvey_add_html = NaayaPageTemplateFile('zpt/megasurvey_add', globals(), 'NaayaSurvey.megasurvey_add') def manage_addMegaSurvey(context, id='', title='', lang=None, REQUEST=None, **kwargs): """ """ title = title or 'Survey' id = make_id(context, id=id, title=title) # Get selected language lang = REQUEST and REQUEST.form.get('lang', None) lang = lang or kwargs.get('lang', context.gl_get_selected_language())
'default_schema': DEFAULT_SCHEMA, 'schema_name': 'NyInfoFolder', '_module': sys.modules[__name__], 'additional_style': AdditionalStyle('www/InfoFolder.css', globals()), 'icon': os.path.join(os.path.dirname(__file__), 'www', 'NyInfoFolder.gif'), 'on_install': setupContentType, '_misc': { 'NyInfoFolder.gif': ImageFile('www/NyInfoFolder.gif', globals()), 'NyInfoFolder_marked.gif': ImageFile('www/NyInfoFolder_marked.gif', globals()), }, } #Portal portlets NaayaPageTemplateFile('zpt/latest_uploads_portlet', globals(), 'naaya.content-sdo.infofolder.latest_uploads_portlet') NaayaPageTemplateFile( 'zpt/infofolder_search_portlet', globals(), 'naaya.content-sdo.infofolder.infofolder_search_portlet') NaayaPageTemplateFile('zpt/events_filter', globals(), 'naaya.content-sdo.infofolder.events_filter') NaayaPageTemplateFile('zpt/submit_site_portlet', globals(), 'naaya.content-sdo.infofolder.submit_site_portlet') def infofolder_add_html(self, REQUEST=None, RESPONSE=None): """ """ form_helper = get_schema_helper_for_metatype(self, config['meta_type']) return self.getFormsTool().getContent( { 'here': self,
def handleUpload(self, file): """ Upload a file from disk. """ filename = getattr(file, 'filename', '') if not filename: return self.manage_delObjects(self.objectIds()) file_id = cookId('', '', file)[0] #cleanup id self.manage_addFile(id=file_id, file=file) InitializeClass(NySemNews) #Custom folder listing NaayaPageTemplateFile('zpt/semnews_folder_index', globals(), 'semnews_folder_index') config.update({ 'constructors': (manage_addNySemNews_html, addNySemNews), 'folder_constructors': [ ('manage_addNySemNews_html', manage_addNySemNews_html), ('semnews_add_html', semnews_add_html), ('addNySemNews', addNySemNews), ('import_NySemNews', importNySemNews), ], 'add_method': addNySemNews, 'validation': issubclass(NySemNews, NyValidation), '_class': NySemNews,
class NaayaTemplateTestCase(NaayaTestCase.NaayaTestCase): def afterSetUp(self): self.my_tmpl = NaayaPageTemplateFile('the_template', globals(), 'my_tmpl') NyDocument.my_tmpl = self.my_tmpl self.second_tmpl = NaayaPageTemplateFile('the_template', globals(), 'second_tmpl') NyDocument.second_tmpl = self.second_tmpl def beforeTearDown(self): del NyDocument.my_tmpl del NyDocument.second_tmpl del FormsTool.naaya_templates['my_tmpl'] del FormsTool.naaya_templates['second_tmpl'] def test_default(self): output = self.my_tmpl.__of__(self.portal)(a="26") self.assertTrue('[physical path: /portal]' in output) self.assertTrue('[option a: 26]' in output) output = self.portal.info.contact.my_tmpl(a="13") self.assertTrue('[physical path: /portal/info/contact]' in output) self.assertTrue('[option a: 13]' in output) def test_customize(self): forms_tool = self.portal.portal_forms ids = [f['id'] for f in forms_tool.listDefaultForms()] self.assertTrue('my_tmpl' in ids) my_tmpl_aq = forms_tool.getForm('my_tmpl') self.assertTrue(my_tmpl_aq.aq_self is NyDocument.my_tmpl) self.assertTrue(my_tmpl_aq.aq_parent is forms_tool) forms_tool.manage_customizeForm('my_tmpl') forms_tool.my_tmpl.pt_edit(text='new content', content_type='text/html') self.assertEqual(self.portal.info.contact.my_tmpl().strip(), 'new content') def test_template_traversal(self): forms_tool = self.portal.getFormsTool() # Customize 2nd template to include a macro def second_tmpl = forms_tool['second_tmpl'] my_tmpl = forms_tool['my_tmpl'] self.assertEqual(second_tmpl, forms_tool.getForm('second_tmpl')) forms_tool.manage_customizeForm('second_tmpl') text = ('<metal:block define-macro="hole">' 'Bugs Bunny</metal:block>') forms_tool.second_tmpl.pt_edit(text=text, content_type='text/html') # Customize 1st template to include previous macro in several ways forms_tool.manage_customizeForm('my_tmpl') text = ('<tal:block metal:use-macro="' "python:here.getFormsTool().getForm('second_tmpl')" ".macros['hole']\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output =self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1) text = ('<tal:block metal:use-macro="' "python:here.getFormsTool()['second_tmpl']" ".macros['hole']\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output = self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1) text = ('<tal:block metal:use-macro="' "here/portal_forms/second_tmpl/" "macros/hole\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output = self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1)
class NaayaPage(DiggPage, Implicit): security = ClassSecurityInfo() security.declareProtected(view, 'pagination') pagination = NaayaPageTemplateFile('zpt/pagination', globals(), 'naaya.core.pagination')
] rows = [] participants = self.getAttendees() for participant in participants: part_info = self.getAttendeeInfo(participant) participant_info = [ part_info['uid'], part_info['name'], part_info['email'], part_info['organization'], part_info['role'], '', part_info['phone'], '', '', '', '', '', '', '', '', '', '', country_from_country_code.get(part_info['country'], ''), '', '', '', part_info['uid'], '' ] rows.append(participant_info) filename = '%s_%s_%s.csv' % (self.getMeeting().getId( ), self.id, datetime.now().strftime("%Y-%m-%d_%H-%M-%S")) RESPONSE.setHeader('Content-Type', 'text/csv') RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s' % filename) return generate_csv(header, rows) InitializeClass(Participants) NaayaPageTemplateFile('zpt/participants_index', globals(), 'naaya.content.meeting.participants_index') NaayaPageTemplateFile('zpt/participants_pickrole', globals(), 'naaya.content.meeting.participants_pickrole') NaayaPageTemplateFile('zpt/participants_table', globals(), 'naaya.content.meeting.participants_table')
class AnalyticsTool(SimpleItem, utils): """ """ meta_type = METATYPE_ANALYTICSTOOL icon = 'misc_/NaayaCore/AnalyticsTool.gif' security = ClassSecurityInfo() _google_access_token = None _google_refresh_token = None profile_code = None profile = None def __init__(self, id, title): """ """ self.id = id self.title = title self._reset() def _reset(self): self.account = None self.date_interval = 30 self.start_date = '' self.ga_id = '' # Google Analytics web property ID (UA-number) self.gw_verify = '' # Google Webmaster verification meta tag self._google_access_token = None self._google_refresh_token = None self.profile_code = None self.profile = None self.clear_cache() #cache def _set_cache(self, data, view_name): self._cache[view_name] = data self._cache_timestamp = datetime.datetime.now() security.declarePrivate('get_cache') def get_cache(self, view_name): interval = datetime.datetime.now() - self._cache_timestamp if interval.days > 0: return None return self._cache.get(view_name, None) security.declarePrivate('get_cache') def clear_cache(self): self._cache = {} self._cache_timestamp = datetime.datetime.now() #administration def index_html(self, REQUEST): """ redirect to admin_account """ REQUEST.RESPONSE.redirect(self.absolute_url() + '/admin_account') _admin_account_zpt = NaayaPageTemplateFile('zpt/account', globals(), 'site_admin_account') _admin_verify = NaayaPageTemplateFile('zpt/verify', globals(), 'site_admin_verify') _stats_info = NaayaPageTemplateFile('zpt/stats_info', globals(), 'site_admin_stats_info') _admin_stats = NaayaPageTemplateFile('zpt/stats', globals(), 'site_admin_stats') security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_stats') def admin_stats(self, REQUEST): """ """ if self._google_access_token is None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/authorize') return elif self.profile is None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/admin_account') return return self._admin_stats() _authorize = NaayaPageTemplateFile('zpt/authorize', globals(), 'site_admin_stats_authorize') def authorize(self): """ """ is_configured = bool('GOOGLE_AUTH_CLIENT_ID' in os.environ and 'GOOGLE_AUTH_CLIENT_SECRET' in os.environ) return self._authorize(is_configured=is_configured) security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'stats_info') def stats_info(self): """ """ view_name = 'stats' cached_data = self.get_cache(view_name=view_name) if cached_data is None: # no data in the cache, so cache it data_to_cache = self._stats_info(self.REQUEST) self._set_cache(data_to_cache, view_name=view_name) return data_to_cache # get cached data return cached_data security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_verify') def admin_verify(self, REQUEST): """ Administration page for Google verification codes """ if REQUEST.has_key('save'): self.ga_id = REQUEST.get('ga_id', '') self.gw_verify = REQUEST.get('gw_verify', '') self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) return self._admin_verify(REQUEST) security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_account') def admin_account(self, REQUEST): """ Administration page for Google accounts """ if self._google_access_token is None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/authorize') return options = {} if self.profile is None: options.update(choose_profile=True, accounts=self.getAccounts()) else: options.update(choose_profile=False, profile_name="") profile_info = self._get_profile_info(self.account, self.profile_code) if profile_info is not None: options['profile_name'] = profile_info['name'] return self._admin_account_zpt(REQUEST, **options) def _update_access_token(self): if self._google_access_token is None: raise RuntimeError("Google access token is not set.") access_token, expiry = self._google_access_token if time.time() > expiry: code = self._google_refresh_token data = { 'grant_type': 'refresh_token', 'client_id': os.environ['GOOGLE_AUTH_CLIENT_ID'], 'client_secret': os.environ['GOOGLE_AUTH_CLIENT_SECRET'], 'refresh_token': code, } resp = requests.post(GOOGLE_TOKEN_URI, data) self._save_access_token(resp) access_token, expiry = self._google_access_token return access_token def _api_get(self, path, params={}): access_token = self._update_access_token() url = GOOGLE_ANALYTICS_API + path headers = {'Authorization': 'Bearer ' + access_token} resp = requests.get(url, params=params, headers=headers) if resp.status_code != 200: raise RuntimeError("API call error: %r (%r)" % (resp, resp.json)) return resp.json security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getAccounts') def getAccounts(self): """ get accounts list """ resp_json = self._api_get('management/accounts') return [(i['id'], i['name']) for i in resp_json['items']] security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getProfiles') def getProfiles(self, account, REQUEST=None): """ """ resp_json = self._api_get('management/accounts/%s/webproperties' % account) data = { 'profiles': [{ 'code': i['id'], 'name': i['name'] } for i in resp_json['items']] } if REQUEST is not None: return json_response(data, REQUEST.RESPONSE) else: return data def _get_profile_info(self, account, profile_code): resp_json = self._api_get('management/accounts/%s/' 'webproperties/%s/profiles' % (account, profile_code)) if 'items' not in resp_json: return None else: return resp_json['items'][0] security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_account_save') def admin_account_save(self, account=None, profile_code=None, date_interval='30', start_date='', REQUEST=None): """ """ if account: self.account = account if profile_code and self.profile is None: # We check `self.profile is None` in order to disallow changing # a profile. De-authorize the account first. profile_info = self._get_profile_info(self.account, profile_code) if profile_info is None: self.setSessionErrorsTrans( "The site you selected is not available.") else: self.profile_code = profile_code self.profile = profile_info['id'] if start_date: self.start_date = start_date self.date_interval = 0 else: self.date_interval = int(date_interval or '30') self.start_date = '' if self.account or self.start_date or self.date_interval: self.clear_cache() #clear cached data self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/admin_account') security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'admin_account_revoke') def admin_account_revoke(self, REQUEST=None): """ """ self._reset() if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/admin_account') security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'generateAuthUrl') def generateAuthUrl(self): """ generate authentication URL """ query = { 'response_type': 'code', 'client_id': os.environ['GOOGLE_AUTH_CLIENT_ID'], 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'scope': GOOGLE_SCOPE, 'access_type': 'offline', } return ('https://accounts.google.com/o/oauth2/auth?' + urllib.urlencode(query)) def _save_access_token(self, resp): if 'error' in resp.json: raise RuntimeError("Error fetching new token: %r" % resp.json['error']) assert resp.json['token_type'] == 'Bearer' expiry = time.time() + resp.json['expires_in'] self._google_access_token = (resp.json['access_token'], expiry) if 'refresh_token' in resp.json: self._google_refresh_token = resp.json['refresh_token'] import transaction transaction.get().note('(Saving new Google oauth2 token)') security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'saveAuthorizationCode') def saveAuthorizationCode(self, code, REQUEST=None): """ """ data = { 'grant_type': 'authorization_code', 'client_id': os.environ['GOOGLE_AUTH_CLIENT_ID'], 'client_secret': os.environ['GOOGLE_AUTH_CLIENT_SECRET'], 'code': code, 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'scope': GOOGLE_SCOPE, } resp = requests.post(GOOGLE_TOKEN_URI, data) self._save_access_token(resp) if REQUEST is not None: REQUEST.RESPONSE.redirect(self.absolute_url() + '/admin_account') def _api_get_ga_data(self, params): sd, ed = self.get_date_interval() params.setdefault('ids', 'ga:' + self.profile) params.setdefault('start-date', sd.strftime('%Y-%m-%d')) params.setdefault('end-date', ed.strftime('%Y-%m-%d')) return self._api_get('data/ga', params=params) security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getVisitsGraph') def getVisitsGraph(self): """ Get the visitors graph """ sd, ed = self.get_date_interval() data = self._api_get_ga_data({ 'dimensions': 'ga:date', 'metrics': 'ga:visits', 'sort': 'ga:date', }) valid = False if 'rows' in data: maximum = 0 res = [] for row in data['rows']: visit_value = int(row[1]) if visit_value > maximum: maximum = visit_value if visit_value and not valid: valid = True #check for 0 values res.append(row[1]) if valid: #chart values, y-axis maxi value, y-axis intermediate values, x-axis labels return ','.join(res), maximum * 1.1, '||%s|%s|%s|%s|' % ( maximum / 3, maximum / 2, 2 * maximum / 3, maximum), '|%s|%s|' % (sd.strftime('%d %b'), ed.strftime('%d %b')) security.declareProtected(view, 'getSiteSummary') def getSiteSummary(self): """ Get esential date about site usage """ view_name = 'summary' if self.profile is None: return None cached_data = self.get_cache(view_name=view_name) if cached_data is not None: return cached_data data = self._api_get_ga_data({ 'metrics': 'ga:visits,ga:visitors,ga:pageviews,ga:timeOnSite', }) if 'rows' in data: #take the first entry [stats] = self._data_rows(data) res = { 'visits': formatter.format(float(stats['ga:visits'])), 'visitors': formatter.format(float(stats['ga:visitors'])), 'pageviews': formatter.format(float(stats['ga:pageviews'])), 'timeOnSite': humanize_time( float(stats['ga:timeOnSite']) / float(stats['ga:visits'])), } # no data in the cache, so cache it self._set_cache(res, view_name=view_name) return res security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getSiteUsage') def getSiteUsage(self): """ Get the site usage """ data = self._api_get_ga_data({ 'metrics': ('ga:visits,ga:bounces,ga:pageviews,' 'ga:timeOnSite,ga:newVisits,ga:entrances'), }) if 'rows' in data: #take the first entry [stats] = self._data_rows(data) bounce_rate = (float(stats['ga:bounces']) / float(stats['ga:entrances']) * 100) pages_visit = (float(stats['ga:pageviews']) / float(stats['ga:visits'])) newVisits = (float(stats['ga:newVisits']) / float(stats['ga:visits']) * 100) return { 'visits': formatter.format(float(stats['ga:visits'])), 'bounces': '%.2f%%' % bounce_rate, 'pages_visit': '%.2f' % pages_visit, 'pageviews': formatter.format(float(stats['ga:pageviews'])), 'timeOnSite': humanize_time( float(stats['ga:timeOnSite']) / float(stats['ga:visits'])), 'newVisits': '%.2f%%' % newVisits, } def _data_rows(self, data): columns = [c['name'] for c in data['columnHeaders']] for row in data['rows']: yield dict(zip(columns, row)) security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getTopPages') def getTopPages(self): """ Get the top pages """ data = self._api_get_ga_data({ 'dimensions': 'ga:pagePath', 'metrics': 'ga:pageviews', 'sort': 'ga:date', 'sort': '-ga:pageviews', 'max_results': '10', }) if 'rows' in data: res = [] for row in self._data_rows(data): res.append({ 'pagePath': row['ga:pagePath'], 'pageviews': formatter.format(float(row['ga:pageviews'])) }) return res, '' security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getTopReferers') def getTopReferers(self): """ Get the top referers """ data = self._api_get_ga_data({ 'dimensions': 'ga:source', 'metrics': 'ga:visits', 'filters': 'ga:medium==referral', 'sort': '-ga:visits', 'max_results': '10', }) if 'rows' in data: res = [] for row in self._data_rows(data): res.append({ 'source': row['ga:source'], 'visits': formatter.format(float(row['ga:visits'])) }) return res security.declareProtected(PERMISSION_PUBLISH_OBJECTS, 'getTopSearches') def getTopSearches(self): """ Get the top searches """ data = self._api_get_ga_data({ 'dimensions': 'ga:keyword', 'metrics': 'ga:visits', 'filters': 'ga:keyword!=(not set)', 'sort': '-ga:visits', 'max_results': '10', }) if 'rows' in data: res = [] for row in self._data_rows(data): res.append({ 'keyword': row['ga:keyword'], 'visits': formatter.format(float(row['ga:visits'])) }) return res security.declarePublic('get_date_interval') def get_date_interval(self): """ """ end_date = datetime.datetime.today() if self.start_date: sd = time.strptime(self.start_date, '%d/%m/%Y') start_date = datetime.datetime(*sd[0:6]) else: start_date = end_date - datetime.timedelta(days=self.date_interval) return start_date, end_date security.declarePublic('get_intervals') def get_intervals(self): """ """ return INTERVALS
class NotificationTool(Folder): """ """ meta_type = core_constants.METATYPE_NOTIFICATIONTOOL icon = 'misc_/NaayaCore/NotificationTool.gif' meta_types = () all_meta_types = meta_types security = ClassSecurityInfo() # default configuration settings default_config = { 'admin_on_error': True, 'admin_on_edit': True, 'enable_instant': True, 'enable_daily': True, 'enable_anonymous': False, # Enable anonymous notifications 'daily_hour': 0, 'enable_weekly': True, 'weekly_day': 1, # 1 = monday, 7 = sunday 'weekly_hour': 0, 'enable_monthly': True, 'monthly_day': 1, # 1 = first day of the month 'monthly_hour': 0, 'notif_content_types': [], } def __init__(self, id, title): """ """ self.id = id self.title = title self.config = PersistentDict(self.default_config) self.timestamps = PersistentDict() # Confirmations list self.pending_anonymous_subscriptions = PersistentList() def get_config(self, key): return self.config.get(key) def get_location_link(self, location): if location: return self.restrictedTraverse(location, self.getSite()).absolute_url() else: return self.getSite().absolute_url() def _validate_subscription(self, **kw): """ Validate add/edit subscription for authorized and anonymous users """ if (kw['notif_type'] not in self.available_notif_types(kw['location']) and not (kw['notif_type'] == 'administrative' and self.checkPermissionPublishObjects())): raise i18n_exception(ValueError, 'Subscribing to ${notif_type} ' 'notifications in "${location}" not allowed', location=kw['location'] or self.getSite().title, notif_type=kw['notif_type']) try: obj = self.getSite().restrictedTraverse(kw['location']) except: raise i18n_exception(ValueError, 'This path is invalid or protected') try: subscription_container = ISubscriptionContainer(obj) except: raise i18n_exception(ValueError, 'Cannot subscribe to this folder') if kw.get('anonymous', False): # Check if subscription exists for this anonymous subscriber if not is_valid_email(kw.get('email', '')): raise i18n_exception( ValueError, 'Your e-mail address does not appear ' 'to be valid.') for id, subscription in subscription_container.list_with_keys(): # Normal subscriptions don't have e-mail if isinstance(subscription, AnonymousSubscription): if (subscription.email == kw['email'] and subscription.notif_type == kw['notif_type'] and subscription.lang == kw['lang']): raise i18n_exception(ValueError, 'Subscription already exists') def _sitemap_dict(self, form): """ Compose a sitemap dict """ node = form.get('node', '') if not node or node == '/': node = '' def traverse(objects, level=0, stop_level=2, exclude_root=False): """ Create a dict with node properties and children. This is a fixed level recursion. On some sites there are a lot of objects so we don't need to get the whole tree. """ res = [] for ob in objects: if ISubscriptionTarget.providedBy(ob) is False: continue children_objects = [] if level != stop_level: # Stop if the level is reached # Create a list of object's children if hasattr(ob, 'objectValues'): # Get only naaya container objects for child in ob.objectValues( self.get_naaya_containers_metatypes()): # Skip unsubmited/unapproved if not getattr(child, 'approved', False): continue elif not getattr(child, 'submitted', False): continue else: children_objects.append(child) if hasattr(ob, 'approved'): icon = ob.approved and ob.icon or ob.icon_marked else: icon = ob.icon children = traverse(children_objects, level + 1, stop_level) if exclude_root: # Return only the children if this is set return children res.append({ 'data': { 'title': self.utStrEscapeHTMLTags( self.utToUtf8(ob.title_or_id())), 'icon': icon }, 'attributes': { 'title': path_in_site(ob) }, 'children': children }) return res if node == '': tree_dict = traverse([self.getSite()]) else: tree_dict = traverse([self.restrictedTraverse(node)], exclude_root=True) return tree_dict security.declarePublic('sitemap') def sitemap(self, REQUEST=None, **kw): """ Return a json (for Ajax tree) representation of published objects marked with `ISubscriptionTarget` including the portal organized in a tree (sitemap) """ form = {} if REQUEST is not None: form = REQUEST.form REQUEST.RESPONSE.setHeader('content-type', 'application/json') else: form.update(kw) return json.dumps(self._sitemap_dict(form)) security.declarePrivate('add_account_subscription') def add_account_subscription(self, user_id, location, notif_type, lang, content_types=[]): """ Subscribe the user `user_id` """ self._validate_subscription(user_id=user_id, location=location, notif_type=notif_type, lang=lang, content_types=content_types) try: self.remove_account_subscription(user_id, location, notif_type, lang) except ValueError: pass obj = self.getSite().restrictedTraverse(location) subscription_container = ISubscriptionContainer(obj) subscription = AccountSubscription(user_id, notif_type, lang, content_types) subscription_container.add(subscription) security.declarePrivate('add_anonymous_subscription') def add_anonymous_subscription(self, **kw): """ Handle anonymous users """ self._validate_subscription(anonymous=True, **kw) subscription = AnonymousSubscription(**kw) # Add to temporary container self.pending_anonymous_subscriptions.append(subscription) # Send email email_tool = self.getSite().getEmailTool() email_from = email_tool.get_addr_from() email_template = EmailPageTemplateFile('emailpt/confirm.zpt', globals()) email_data = email_template.render_email(**{ 'key': subscription.key, 'here': self }) email_to = subscription.email email_tool.sendEmail(email_data['body_text'], email_to, email_from, email_data['subject']) security.declarePrivate('remove_account_subscription') def remove_account_subscription(self, user_id, location, notif_type, lang, content_types=None): obj = self.getSite().restrictedTraverse(location) subscription_container = ISubscriptionContainer(obj) n = utils.match_account_subscription(subscription_container, user_id, notif_type, lang, content_types) if n is None: raise ValueError('Subscription not found') subscription_container.remove(n) security.declarePrivate('unsubscribe_links_html') unsubscribe_links_html = PageTemplateFile("emailpt/unsubscribe_links.zpt", globals()) security.declarePrivate('remove_anonymous_subscription') def remove_anonymous_subscription(self, email, location, notif_type, lang): try: obj = self.getSite().restrictedTraverse(location) except: raise i18n_exception(ValueError, 'Invalid location') try: subscription_container = ISubscriptionContainer(obj) except: raise i18n_exception(ValueError, 'Invalid container') anonymous_subscriptions = [ (n, s) for n, s in subscription_container.list_with_keys() if hasattr(s, 'email') ] subscriptions = filter( lambda s: (s[1].email == email and s[1].location == location and s[ 1].notif_type == notif_type), anonymous_subscriptions) if len(subscriptions) == 1: subscription_container.remove(subscriptions[0][0]) else: raise i18n_exception(ValueError, 'Subscription not found') security.declareProtected(view, 'available_notif_types') def available_notif_types(self, location=''): if self.config['enable_instant']: yield 'instant' if self.config['enable_daily']: yield 'daily' if self.config['enable_weekly']: yield 'weekly' if self.config['enable_monthly']: yield 'monthly' security.declarePrivate('notify_maintainer') def notify_maintainer(self, ob, folder, **kwargs): """ Process and notify by email that B{p_object} has been uploaded into the B{p_folder}. """ auth_tool = self.getSite().getAuthenticationTool() emails = self.getMaintainersEmails(ob) person = self.REQUEST.AUTHENTICATED_USER.getUserName() if len(emails) > 0: maintainers_data = {} for email in emails: maintainers_data[email] = { 'ob': ob, 'here': self, 'person': auth_tool.name_from_userid(person), 'ob_edited': kwargs.get('ob_edited'), 'approved': ob.approved, 'container_basket': '%s/basketofapprovals_html' % folder.absolute_url(), } notif_logger.info('Maintainer notifications on %r', ofs_path(ob)) template = self._get_template('maintainer') self._send_notifications(maintainers_data, template) security.declarePrivate('notify_comment_maintainer') def notify_comment_maintainer(self, comment, parent, **kwargs): """ Process and notify by email that a comment B{comemnt} has been added to the object B{parent}. """ auth_tool = self.getSite().getAuthenticationTool() emails = self.getMaintainersEmails(parent) if len(emails) > 0: maintainers_data = {} for email in emails: maintainers_data[email] = { 'parent': parent, 'here': self, 'comment': comment, 'person': auth_tool.name_from_userid(comment.author), 'container_basket': '%s/basketofapprovals_html' % parent.absolute_url(), } notif_logger.info('Maintainer comment notifications on %r', ofs_path(parent)) template = self._get_template('maintainer') self._send_notifications(maintainers_data, template) security.declarePrivate('notify_administrative') def notify_administrative(self, ob, user_id, ob_edited=False): """ send administrative notifications because object `ob` was added or edited by the user `user_id` """ auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, ob, notif_type='administrative', **{ 'person': auth_tool.name_from_userid(user_id), 'ob_edited': ob_edited, 'approved': ob.approved, 'container_basket': '%s/basketofapprovals_html' % ob.aq_parent.absolute_url(), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Administrative notifications on %r', ofs_path(ob)) template = self._get_template('administrative') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_comment_administrative') def notify_comment_administrative(self, comment, parent, user_id): """ send administrative notifications because a comment was added to object `ob` by the user `user_id` """ auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, parent, notif_type='administrative', **{ 'comment': comment, 'parent': parent, 'here': self, 'person': auth_tool.name_from_userid(user_id), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Administrative comment notifications on %r', ofs_path(parent)) template = self._get_template('administrative') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_instant') def notify_instant(self, ob, user_id, ob_edited=False): """ send instant notifications because object `ob` was changed by the user `user_id` """ if not self.config['enable_instant']: return # Don't send notifications if the object is unapproved, but store them # into a queue to send them later when it becomes approved if not ob.approved: return auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, ob, **{ 'person': auth_tool.name_from_userid(user_id), 'ob_edited': ob_edited, }) if len(subscribers_data.keys()) > 0: notif_logger.info('Instant notifications on %r', ofs_path(ob)) template = self._get_template('instant') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_comment_instant') def notify_comment_instant(self, comment, parent, user_id): """ send instant notifications because a comment was added to object `ob` by the user `user_id` """ if not self.config['enable_instant']: return # Don't send notifications if the object is unapproved, but store them # into a queue to send them later when it becomes approved if not parent.approved: return auth_tool = self.getSite().getAuthenticationTool() subscribers_data = utils.get_subscribers_data( self, parent, **{ 'comment': comment, 'parent': parent, 'person': auth_tool.name_from_userid(user_id), }) if len(subscribers_data.keys()) > 0: notif_logger.info('Comment instant notifications on %r', ofs_path(parent)) template = self._get_template('instant') self._send_notifications(subscribers_data, template) security.declarePrivate('notify_account_modification') def notify_account_modification(self, email, obj, username=None, new_roles=[], removed_roles=[]): """ Send notification that the user received or lost one or more roles in the specified location """ email_data = { email: { 'new_roles': new_roles, 'removed_roles': removed_roles, 'username': username, 'obj': obj, } } notif_logger.info('Account modification notification on %s' % self.getSite().getId()) template = self._get_template('account_modified') self._send_notifications(email_data, template) def _get_template(self, name): template = self._getOb('emailpt_%s' % name, None) if template is not None: return template.render_email template = self._getOb(name, None) if template is not None: return template.render_email template = email_templates.get(name, None) if template is not None: return template.render_email raise ValueError('template for %r not found' % name) def _send_notifications(self, messages_by_email, template): """ Send the notifications described in the `messages_by_email` data structure, using the specified EmailTemplate. `messages_by_email` should be a dictionary, keyed by email address. The values should be dictionaries suitable to be passed as kwargs (options) to the template. """ portal = self.getSite() email_tool = portal.getEmailTool() addr_from = email_tool.get_addr_from() for addr_to, kwargs in messages_by_email.iteritems(): translate = self.portal_i18n.get_translation kwargs.update({'portal': portal, '_translate': translate}) mail_data = template(**kwargs) notif_logger.info('.. sending notification to %r', addr_to) utils.send_notification(email_tool, addr_from, addr_to, mail_data['subject'], mail_data['body_text']) def _send_newsletter(self, notif_type, when_start, when_end): """ We'll look in the ``Products.Naaya.NySite.getActionLogger`` for object creation/modification log entries. Then we'll send notifications for the period between `when_start` and `when_end` using the `notif_type` template. """ notif_logger.info( 'Notifications newsletter on site %r, type %r, ' 'from %s to %s', ofs_path(self.getSite()), notif_type, when_start, when_end) objects_by_email = {} langs_by_email = {} subscriptions_by_email = {} anonymous_users = {} for log_type, ob in utils.get_modified_objects(self.getSite(), when_start, when_end): notif_logger.info('.. modified object: %r', ofs_path(ob)) for subscription in utils.fetch_subscriptions(ob, inherit=True): if subscription.notif_type != notif_type: continue if not subscription.check_permission(ob): continue email = subscription.get_email(ob) if email is None: continue content_types = getattr(subscription, 'content_types', []) if content_types and ob.meta_type not in content_types: continue notif_logger.info('.. .. sending newsletter to %r', email) objects_by_email.setdefault(email, []).append({ 'ob': ob, 'type': log_type, }) langs_by_email[email] = subscription.lang subscriptions_by_email[email] = subscription anonymous_users[email] = isinstance(subscription, AnonymousSubscription) messages_by_email = {} for email in objects_by_email: messages_by_email[email] = { 'objs': objects_by_email[email], '_lang': langs_by_email[email], 'subscription': subscriptions_by_email[email], 'here': self, 'anonymous': anonymous_users[email] } template = self._get_template(notif_type) self._send_notifications(messages_by_email, template) def _cron_heartbeat(self, when): transaction.commit() # commit earlier stuff; fresh transaction transaction.get().note('notifications cron at %s' % ofs_path(self)) # Clean temporary subscriptions after a week: if self.config.get('enable_anonymous', False): a_week_ago = when - timedelta(weeks=1) for tmp_subscription in self.pending_anonymous_subscriptions[:]: if tmp_subscription.datetime <= a_week_ago: self.pending_anonymous_subscriptions.remove( tmp_subscription) # daily newsletter ### if self.config['enable_daily']: # calculate the most recent daily newsletter time daily_time = time(hour=self.config['daily_hour']) latest_daily = datetime.combine(when.date(), daily_time) if latest_daily > when: latest_daily -= timedelta(days=1) # check if we should send a daily newsletter prev_daily = self.timestamps.get('daily', when - timedelta(days=1)) if prev_daily < latest_daily < when: self._send_newsletter('daily', prev_daily, when) self.timestamps['daily'] = when # weekly newsletter ### if self.config['enable_weekly']: # calculate the most recent weekly newsletter time weekly_time = time(hour=self.config['daily_hour']) t = datetime.combine(when.date(), weekly_time) days_delta = self.config['weekly_day'] - t.isoweekday() latest_weekly = t + timedelta(days=days_delta) if latest_weekly > when: latest_weekly -= timedelta(weeks=1) # check if we should send a weekly newsletter prev_weekly = self.timestamps.get('weekly', when - timedelta(weeks=1)) if prev_weekly < latest_weekly < when: self._send_newsletter('weekly', prev_weekly, when) self.timestamps['weekly'] = when # monthly newsletter ### if self.config['enable_monthly']: # calculate the most recent monthly newsletter time monthly_time = time(hour=self.config['monthly_hour']) the_day = utils.set_day_of_month(when.date(), self.config['monthly_day']) latest_monthly = datetime.combine(the_day, monthly_time) if latest_monthly > when: latest_monthly = utils.minus_one_month(latest_monthly) # check if we should send a monthly newsletter prev_monthly = self.timestamps.get('monthly', utils.minus_one_month(when)) if prev_monthly < latest_monthly < when: self._send_newsletter('monthly', prev_monthly, when) self.timestamps['monthly'] = when transaction.commit() # make sure our timestamp updates are saved def index_html(self, RESPONSE): """ redirect to admin page """ RESPONSE.redirect(self.absolute_url() + '/my_subscriptions_html') security.declareProtected(view, 'my_subscriptions_html') my_subscriptions_html = NaayaPageTemplateFile( 'zpt/index', globals(), 'naaya.core.notifications.my_subscriptions') security.declarePrivate('list_user_subscriptions') def user_subscriptions(self, user, cutoff_level=None): """ Returns all user subscriptions in the portal. Use with caution as this iterates almost all the objects in site. You can use `cutoff_level` to limit the depth. """ out = [] user_id = user.getId() for obj, n, subscription in utils.walk_subscriptions( self.getSite(), cutoff_level): if not isinstance(subscription, AccountSubscription): continue if subscription.user_id != user_id: continue out.append({ 'object': obj, 'notif_type': subscription.notif_type, 'content_types': getattr(subscription, 'content_types', []), 'lang': subscription.lang }) return out security.declareProtected(view, 'user_not_found') def user_not_found(self, REQUEST): """ Returns True if the user is not Anonymous, but is still not found by the AuthenticationTool (i.e. is maybe defined in the Zope root) """ user = REQUEST.AUTHENTICATED_USER if not isinstance(user, basestring): # with LDAP authentication, user is LDAP user instance user = user.id acl_tool = self.getAuthenticationTool() if acl_tool.get_user_with_userid(user) is None: return True security.declareProtected(view, 'list_my_subscriptions') def list_my_subscriptions(self, REQUEST): """ Returns a list of mappings (location, notif_type, lang) for all subscriptions of logged-in user """ user = REQUEST.AUTHENTICATED_USER if user.getId() is None and not self.config.get( 'enable_anonymous', False): raise Unauthorized # to force login subscriptions = self.user_subscriptions(user) for subscription in subscriptions: subscription['location'] = path_in_site(subscription['object']) del subscription['object'] return subscriptions security.declareProtected(view, 'my_first_subscription') def get_location_subscription(self, location, notif_type=None): """ Returns the first of the authenticated user's subscriptions in location """ for subscription in self.list_my_subscriptions(self.REQUEST): if subscription['location'] == location: if notif_type: if subscription['notif_type'] == notif_type: return subscription else: return subscription security.declareProtected(view, 'subscribe_me') def subscribe_me(self, REQUEST, location, notif_type, lang=None, content_types=[]): """ add subscription for currently-logged-in user """ # Even if some content types were selected (by turning off javascript) # they should be ignored, no filtering in administrative notifications if notif_type == 'administrative': content_types = [] if isinstance(content_types, basestring): content_types = [content_types] if lang is None: lang = self.gl_get_selected_language() REQUEST.form['lang'] = lang user_id = REQUEST.AUTHENTICATED_USER.getId() if location == '/': location = '' if user_id is None and not self.config.get('enable_anonymous', False): raise Unauthorized # to force login try: if user_id: self.add_account_subscription(user_id, location, notif_type, lang, content_types) if content_types: self.setSessionInfoTrans( 'You will receive ${notif_type} notifications' ' for any changes in "${location}" for objects of ' 'types ${content_types}.', notif_type=notif_type, location=location or self.getSite().title, content_types=', '.join(content_types)) else: self.setSessionInfoTrans( 'You will receive ${notif_type} notifications' ' for any changes in "${location}".', notif_type=notif_type, location=location) else: self.add_anonymous_subscription(**dict(REQUEST.form)) self.setSessionInfoTrans( 'An activation e-mail has been sent to ${email}. ' 'Follow the instructions to subscribe to ${notif_type} ' 'notifications for any changes in "${location}".', notif_type=notif_type, location=location, content_types=content_types, email=REQUEST.form.get('email')) except ValueError, msg: self.setSessionErrors([unicode(msg)]) return REQUEST.RESPONSE.redirect(self.absolute_url() + '/my_subscriptions_html')
from Products.NaayaCore.FormsTool.NaayaTemplate import NaayaPageTemplateFile _admin_assign_role = NaayaPageTemplateFile('zpt/site_admin_editor_role', globals(), 'site_admin_editor_role') def admin_assign_role(context, REQUEST): orig_id = REQUEST.get('orig_id') object_paths = REQUEST.get('object_paths', []) new_users = REQUEST.get('new_users', []) messages = [] options = {} user_list = context.getAuthenticationTool().getUsers() users = {} for user in user_list: users[user.name] = { 'name': user.firstname + ' ' + user.lastname, 'email': user.email } user_ids = sorted(users.keys(), key=lambda x: x.lower()) if orig_id == '': messages.append('Please select a user to search for content.') orig_id = None options['users'] = users options['user_ids'] = user_ids if not orig_id: options['messages'] = messages return _admin_assign_role.__of__(context)(REQUEST, **options) if REQUEST.has_key('assign_role'): if isinstance(object_paths, basestring): object_paths = [object_paths]
security.declarePublic('checkPermissionAddAnswer') def checkPermissionAddAnswer(self): """Check if the user has the ADD_ANSWER permission""" return self.checkPermission(PERMISSION_ADD_ANSWER) def checkPermissionEditAnswers(self): """ Check if the user has EDIT_ANSWER permission""" return self.checkPermission(PERMISSION_EDIT_ANSWERS) # # Site pages # security.declareProtected(PERMISSION_VIEW_REPORTS, 'view_reports_html') view_reports_html = NaayaPageTemplateFile( 'zpt/questionnaire_view_reports', globals(), 'NaayaSurvey.questionnaire_view_reports') security.declareProtected(PERMISSION_VIEW_ANSWERS, 'view_answers_html') view_answers_html = NaayaPageTemplateFile( 'zpt/questionnaire_view_answers', globals(), 'NaayaSurvey.questionnaire_view_answers') manage_main = folder_manage_main_plus ny_before_listing = PageTemplateFile('zpt/questionnaire_manage_header', globals()) security.declareProtected(view_management_screens, 'manage_create_validation_html') def manage_create_validation_html(self, REQUEST=None):
class NyGlossaryElement(SimpleItem, ElementBasic, utils, catalog_utils): """ NyGlossaryElement """ interface.implements(INyGlossaryElement) meta_type = NAAYAGLOSSARY_ELEMENT_METATYPE meta_label = LABEL_OBJECT product_name = NAAYAGLOSSARY_PRODUCT_NAME icon = 'misc_/NaayaGlossary/element.gif' manage_options = ( { 'label': 'Translations', 'action': 'translations_html' }, { 'label': 'Properties', 'action': 'properties_html' }, { 'label': "View", 'action': 'index_html' }, { 'label': 'Undo', 'action': 'manage_UndoForm' }, ) security = ClassSecurityInfo() def __init__(self, id, title, source, subjects, contributor, approved): """ constructor """ self.id = id self.subjects = subjects self.approved = approved ElementBasic.__dict__['__init__'](self, title, source, contributor) def is_published(self): return self.approved ##################### # BASIC PROPERTIES # ##################### security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'manageBasicProperties') def manageBasicProperties(self, title='', source='', subjects=[], contributor='', approved=0, further_references='', REQUEST=None): """ manage basic properties for NyGlossaryElement """ self.title = title self.source = source self.subjects = self.get_subject_by_codes(subjects) self.contributor = contributor self.approved = approved self.further_references = further_references self._p_changed = 1 self.cu_recatalog_object(self) if REQUEST: return REQUEST.RESPONSE.redirect('properties_html?save=ok') security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'approveElement') def approveElement(self, REQUEST=None): """ used for approval link in basket of approvals""" self.approved = 1 if REQUEST: return REQUEST.RESPONSE.redirect('index_approvals_html') ######################### # THEME FUNCTIONS # ######################### def code_in_subjects(self, code): """ check if code is in the list """ for subj_info in self.subjects: if subj_info['code'] == code: return 1 return 0 def get_subjects(self): """ get the languages """ self.utSortListOfDictionariesByKey(self.subjects, 'code') return self.subjects security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'set_subjects') def set_subjects(self, code, name): """ set the languages """ append = self.subjects.append append({'code': code, 'name': name}) security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'del_subject') def del_subject(self, code): """ remove a language from list """ for subj_info in self.subjects: if subj_info['code'] == code: self.subjects.remove(subj_info) ################################# # NAME TRANSLATIONS FUNCTIONS # ################################# def get_translation_by_language(self, language): """ get translation by language """ try: return getattr(self.aq_base, language) except: return '' def get_translation_by_language_for_js(self, language): """ get translation by language for the javascript code""" try: translation = self.get_translation_by_language(language) if not translation: translation = self.title_or_id() except AttributeError: translation = self.title_or_id() return translation.replace('_', ' ') def check_if_no_translations(self): """ check if translation """ for lang in self.get_english_names(): if self.get_translation_by_language(lang) != '': return 1 return 0 security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'set_translations_list') def set_translations_list(self, language, translation): """ set the languages """ real_self = self.aq_base if getattr(real_self, language, u"") == translation: # no need to do anything, so let's avoid generating a transaction return if translation == "": if hasattr(real_self, language): delattr(real_self, language) else: setattr(real_self, language, translation) event.notify(ItemTranslationChanged(self, language, translation)) def load_translations_list(self): """ load languages """ for lang in self.get_english_names(): self.set_translations_list(lang, '') security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'manageNameTranslations') def manageNameTranslations(self, lang_code='', translation='', REQUEST=None): """ save translation for a language """ self.set_translations_list(lang_code, translation) if REQUEST: return REQUEST.RESPONSE.redirect('translations_html?tab=0') ####################################### # DEFINITION TRANSLATIONS FUNCTIONS # ####################################### def get_def_trans_by_language(self, language): """ get translation by language """ return getattr(self.aq_base, self.definition_lang(language), '') def check_if_no_def_trans(self): """ check if translation """ for lang in self.get_english_names(): if self.get_def_trans_by_language(lang) != '': return 1 return 0 security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'set_def_trans_list') def set_def_trans_list(self, language, translation): """ set the languages """ self.set_translations_list(self.definition_lang(language), translation) def load_def_trans_list(self): """ load languages """ for lang in self.get_english_names(): self.set_translations_list(self.definition_lang(lang), '') security.declareProtected(PERMISSION_MANAGE_NAAYAGLOSSARY, 'manageDefinitionTranslations') def manageDefinitionTranslations(self, lang_code='', translation='', REQUEST=None): """ save translation for a language """ self.set_def_trans_list(lang_code, translation) if REQUEST: return REQUEST.RESPONSE.redirect('translations_html?tab=1') ##################### # MANAGEMENT TABS # ##################### security.declareProtected(view_management_screens, 'translations_html') translations_html = PageTemplateFile( "zpt/NaayaGlossaryElement/translations", globals()) security.declareProtected(view_management_screens, 'name_trans_html') name_trans_html = PageTemplateFile("zpt/NaayaGlossaryElement/name_trans", globals()) security.declareProtected(view_management_screens, 'definition_trans_html') definition_trans_html = PageTemplateFile( "zpt/NaayaGlossaryElement/definition_trans", globals()) security.declareProtected(view_management_screens, 'properties_html') properties_html = NaayaPageTemplateFile( 'zpt/NaayaGlossaryElement/properties', globals(), 'glossary_element_properties') view_elements_html = PageTemplateFile( "zpt/NaayaGlossaryElement/view_elements", globals()) index_html = NaayaPageTemplateFile("zpt/NaayaGlossaryElement/index", globals(), 'glossary_element_index') ################# # SITE MAP # ################# security.declarePublic('getGlossaryObTree') def getGlossaryObTree(self): """ """ return None security.declareProtected(view_management_screens, 'manage_tabs') def manage_tabs(self): # we override manage_tabs to insert warning about synchronized glossary if self.sync_remote_url: extra_html = self.sync_info_text(zmi=True) else: extra_html = '' return super(NyGlossaryElement, self).manage_tabs() + extra_html
class NaayaTemplateTestCase(NaayaTestCase.NaayaTestCase): def afterSetUp(self): self.my_tmpl = NaayaPageTemplateFile('the_template', globals(), 'my_tmpl') self.second_tmpl = NaayaPageTemplateFile('the_template', globals(), 'second_tmpl') NyDocument.my_tmpl = self.my_tmpl NyDocument.second_tmpl = self.second_tmpl def beforeTearDown(self): del NyDocument.my_tmpl del NyDocument.second_tmpl def test_render(self): """ Render templates by calling the template """ output = self.my_tmpl.__of__(self.portal)(a="26") self.assertTrue('[physicalpath: /portal]' in output) self.assertTrue('[optiona: 26]' in output) output = self.portal.info.contact.my_tmpl(a="13") self.assertTrue('[physicalpath: /portal/info/contact]' in output) self.assertTrue('[optiona: 13]' in output) def test_get_content(self): """ Test getContent """ forms_tool = self.portal.portal_forms forms_tool.manage_customizeForm('my_tmpl') forms_tool.my_tmpl.pt_edit(text='<tal:block replace="a" />', content_type='text/html') output = forms_tool.getContent({'a': 26 }, 'my_tmpl') self.assertTrue('26' in output) def test_customized_templates(self): forms_tool = self.portal.portal_forms forms_tool.manage_customizeForm('my_tmpl') self.assertTrue('my_tmpl' in forms_tool.customized_templates()) def test_customize(self): forms_tool = self.portal.portal_forms self.assertTrue('my_tmpl' in dict(forms_tool.get_all_templates())) my_tmpl_aq = forms_tool.getForm('my_tmpl') self.assertTrue(my_tmpl_aq.aq_self is NyDocument.my_tmpl) self.assertTrue(my_tmpl_aq.aq_parent is forms_tool) forms_tool.manage_customizeForm('my_tmpl') forms_tool.my_tmpl.pt_edit(text='new content', content_type='text/html') self.assertEqual(self.portal.info.contact.my_tmpl().strip(), 'new content') def test_template_traversal(self): forms_tool = self.portal.getFormsTool() # Customize 2nd template to include a macro def second_tmpl = forms_tool.getForm('second_tmpl') self.assertEqual(second_tmpl, forms_tool.getForm('second_tmpl')) forms_tool.manage_customizeForm('second_tmpl') text = ('<metal:block define-macro="hole">' 'Bugs Bunny</metal:block>') forms_tool.second_tmpl.pt_edit(text=text, content_type='text/html') # Customize 1st template to include previous macro in several ways forms_tool.manage_customizeForm('my_tmpl') text = ('<tal:block metal:use-macro="' "python:here.getFormsTool().getForm('second_tmpl')" ".macros['hole']\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output =self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1) text = ('<tal:block metal:use-macro="' "python:here.getFormsTool()['second_tmpl']" ".macros['hole']\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output = self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1) text = ('<tal:block metal:use-macro="' "here/portal_forms/second_tmpl/" "macros/hole\"></tal:block>") forms_tool.my_tmpl.pt_edit(text=text, content_type='text/html') output = self.my_tmpl.__of__(self.portal)() self.assertTrue(output.find("Bugs Bunny")>-1)
class CommentsAdmin(SimpleItem): security = ClassSecurityInfo() title = "Comments administration" def __init__(self, id): self.id = id def _iter_comments(self): for section in self.list_sections(): for paragraph in section.get_paragraphs(): for comment in sorted(paragraph.get_comments(), key=lambda c: c.comment_date): yield comment _admin_template = NaayaPageTemplateFile('zpt/comments_admin', globals(), 'tbconsultation_comments_admin') security.declarePublic('index_html') def index_html(self, REQUEST): """ the admin page for comments """ if self.checkPermissionManageTalkBackConsultation(): pass elif self.checkPermissionInviteToTalkBackConsultation(): pass elif self.own_comments(): pass else: raise Unauthorized total_count = 0 invited_count = 0 anonymous_count = 0 for comment in self._iter_comments(): total_count += 1 if comment.is_invited: invited_count += 1 elif comment.is_anonymous: anonymous_count += 1 contributors = self._get_contributors_stats() invited = filter(lambda x: x['invited'], contributors) anonymous = filter(lambda x: x['anonymous'], contributors) options = { 'contributors_count': len(contributors), 'invited_contributors_count': len(invited), 'anonymous_contributors_count': len(anonymous), 'contributors_details': contributors, 'total_count': total_count, 'invited_count': invited_count, 'anonymous_count': anonymous_count, 'comment_macros': Paragraph.comments_html.macros, 'get_comments_trend': self.get_comments_trend(), } return self._admin_template(REQUEST, **options) def _get_contributors_stats(self): contrib_stats = {} for comment in self._iter_comments(): try: test = contrib_stats[comment.contributor] except KeyError: new_user_info = comment.get_contributor_info() new_user_info['count'] = 1 contrib_stats[comment.contributor] = new_user_info else: contrib_stats[comment.contributor]['count'] += 1 contrib_stats = [v for k, v in contrib_stats.items()] return contrib_stats security.declareProtected(PERMISSION_MANAGE_TALKBACKCONSULTATION, 'get_comments_trend') def get_comments_trend(self): """ get comments trend """ days = {self.start_date.Date(): 0, self.end_date.Date(): 0} for comment in self._iter_comments(): date = comment.comment_date.Date() count = days.setdefault(date, 0) days[date] = count + 1 data = [] for day, comments in days.items(): data.append({'day': day, 'comments': comments}) return json.dumps(sorted(data, key=lambda x: x['day'])) security.declarePrivate('generate_csv_output') def generate_csv_output(self, fields, comments): output = StringIO() csv_writer = csv.writer(output) csv_writer.writerow([ 'Consultation deadline', (self.end_date + 1).strftime('%Y/%m/%d %H:%M') ]) csv_writer.writerow( ['Date of export', DateTime().strftime('%Y/%m/%d %H:%M')]) csv_writer.writerow([field[0] for field in fields]) for n, item in enumerate(comments): row = [field[1](item).encode('utf-8') for field in fields] csv_writer.writerow(row) return codecs.BOM_UTF8 + output.getvalue() security.declarePrivate('generate_excel_output') def generate_excel_output(self, fields, comments): header_style = xlwt.easyxf('font: bold on; align: horiz left;') normal_style = xlwt.easyxf('align: horiz left, vert top;') wb = xlwt.Workbook(encoding='utf-8') ws = wb.add_sheet('Sheet 1') ws.row(0).set_cell_text(0, 'Consultation deadline', header_style) ws.row(0).set_cell_text(1, (self.end_date + 1).strftime('%Y/%m/%d %H:%M'), normal_style) ws.row(1).set_cell_text(0, 'Date of export', header_style) ws.row(1).set_cell_text(1, DateTime().strftime('%Y/%m/%d %H:%M'), normal_style) row = 2 for col in range(len(fields)): ws.row(row).set_cell_text(col, fields[col][0], header_style) for item in comments: row += 1 for col in range(len(fields)): ws.row(row).set_cell_text(col, fields[col][1](item), normal_style) output = StringIO() wb.save(output) return output.getvalue() security.declarePrivate('generate_custom_excel_output') def generate_custom_excel_output(self, fields, comments): normal_style = xlwt.easyxf('align: wrap on, horiz left, vert top;') header_style = xlwt.easyxf( 'font: bold on; align: wrap on, horiz left;') wb = xlwt.Workbook(encoding='utf-8') ws = wb.add_sheet('Sheet 1') row = 0 for col in range(len(fields)): ws.row(row).set_cell_text(col, fields[col][0], header_style) ws.col(0).width = 3000 ws.col(1).width = 20000 ws.col(2).width = 7500 ws.col(3).width = 5000 for item in comments: row += 1 for col in range(len(fields)): ws.row(row).set_cell_text(col, fields[col][1](item), normal_style) row += 1 ws.row(row).set_cell_text(0, 'EEA Comments', header_style) output = StringIO() wb.save(output) return output.getvalue() def _all_comments(self): def replies(this_comment): yield this_comment for child_comment in this_comment['children']: for c in replies(child_comment): yield c for section in self.list_sections(): for paragraph in section.get_paragraphs(): for top_comment in paragraph.get_comment_tree(): for c in replies(top_comment): yield c def all_comments(self): perm_manage = self.checkPermissionManageTalkBackConsultation() if perm_manage: return self._all_comments() own_comments = self.own_comments() if own_comments: return own_comments raise Unauthorized def export(self, file_type="CSV", as_attachment=False, REQUEST=None): """ """ perm_manage = self.checkPermissionManageTalkBackConsultation() if not (perm_manage or self.own_comments): raise Unauthorized html2text = self.getSite().html2text def plain(s, trim=None): return html2text(s, trim) fields = [ ('Section', lambda i: i['comment'].get_section().title_or_id()), ('Paragraph', lambda i: (i['comment'].get_paragraph().plaintext_summary())), ('Message Id', lambda i: i['comment'].getId()), ('Replies', lambda i: str(len(i['children']))), ('In reply to', lambda i: i['comment'].reply_to or ''), ('Message', lambda i: plain(i['comment'].message)), ('Contributor', lambda i: i['comment'].get_contributor_name()), ('Date', lambda i: (i['comment'].comment_date.strftime('%Y/%m/%d %H:%M'))), ('Paragraph url', lambda i: (i['comment'].get_paragraph().absolute_url())), ] comments = self.all_comments() if file_type == 'CSV': ret = self.generate_csv_output(fields, comments) content_type = 'text/csv; charset=utf-8' filename = 'comments.csv' elif file_type == 'Excel': ret = self.generate_excel_output(fields, comments) content_type = 'application/vnd.ms-excel' filename = 'comments.xls' elif file_type == 'CustomExcel': fields = [ ('Section', lambda c: c.get_section().title_or_id()), ('Message', lambda c: plain(c.message)), ('Contributor', lambda c: c.get_contributor_name()), ('Date', lambda c: c.comment_date.strftime('%Y/%m/%d %H:%M')), ] ret = self.generate_custom_excel_output(fields, comments) content_type = 'application/vnd.ms-excel' filename = 'comments.xls' else: raise ValueError('unknown file format %r' % file_type) if as_attachment and REQUEST is not None: filesize = len(ret) set_response_attachment(REQUEST.RESPONSE, filename, content_type, filesize) return ret _comments_table_html = NaayaPageTemplateFile( 'zpt/comments_table', globals(), 'tbconsultation_comments_table') def comments_table_html(self): """ table showing all comments from all participants (manager) or all the user's comments, if the user is just a commenter """ if self.checkPermissionManageTalkBackConsultation(): pass elif self.own_comments(): pass else: raise Unauthorized return self._comments_table_html()
class SurveyQuestionnaire(NyRoleManager, NyAttributes, questionnaire_item, NyContainer): """ """ meta_type = "Naaya Survey Questionnaire" meta_label = "Survey Instance" icon = 'misc_/NaayaSurvey/NySurveyQuestionnaire.gif' icon_marked = 'misc_/NaayaSurvey/NySurveyQuestionnaire_marked.gif' _constructors = () all_meta_types = () manage_options = ( { 'label': 'Contents', 'action': 'manage_main', 'help': ('OFSP', 'ObjectManager_Contents.stx') }, { 'label': 'Properties', 'action': 'manage_propertiesForm', 'help': ('OFSP', 'Properties.stx') }, { 'label': 'View', 'action': 'index_html' }, { 'label': 'Migrations', 'action': 'manage_migrate_html' }, { 'label': 'Updates', 'action': 'manage_update_combo_answers_html' }, { 'label': 'Security', 'action': 'manage_access', 'help': ('OFSP', 'Security.stx') }, ) security = ClassSecurityInfo() notify_owner = True notify_respondents = 'LET_THEM_CHOOSE_YES' allow_overtime = 0 allow_drafts = False allow_anonymous = False allow_multiple_answers = False def __init__(self, id, survey_template, lang=None, **kwargs): """ @param id: id @param survey_template: id of the survey template """ self.id = id self._survey_template = survey_template self.save_properties(lang=lang, **kwargs) NyContainer.__dict__['__init__'](self) self.imageContainer = NyImageContainer(self, True) # # Self edit methods # security.declareProtected(PERMISSION_EDIT_OBJECTS, 'saveProperties') def saveProperties(self, REQUEST=None, **kwargs): """ """ if REQUEST: kwargs.update(REQUEST.form) kwargs.setdefault('contributor', REQUEST.AUTHENTICATED_USER.getUserName()) lang = kwargs.get('lang', self.get_selected_language()) kwargs.setdefault('title', '') kwargs.setdefault('description', '') kwargs.setdefault('keywords', '') kwargs.setdefault('coverage', '') kwargs.setdefault('sortorder', DEFAULT_SORTORDER) releasedate = kwargs.get('releasedate', DateTime()) releasedate = self.process_releasedate(releasedate) kwargs['releasedate'] = releasedate expirationdate = kwargs.get('expirationdate', DateTime()) expirationdate = self.process_releasedate(expirationdate) kwargs['expirationdate'] = expirationdate self.save_properties(**kwargs) self.updatePropertiesFromGlossary(lang) self.recatalogNyObject(self) if REQUEST: # Log date contributor = REQUEST.AUTHENTICATED_USER.getUserName() auth_tool = self.getAuthenticationTool() auth_tool.changeLastPost(contributor) # Redirect self.setSessionInfoTrans(MESSAGE_SAVEDCHANGES, date=self.utGetTodayDate()) REQUEST.RESPONSE.redirect('%s/edit_html?lang=%s' % (self.absolute_url(), lang)) # # Methods required by the Naaya framework # security.declareProtected(view, 'hasVersion') def hasVersion(self): """ """ return False security.declareProtected(view, 'getVersionLocalProperty') def getVersionLocalProperty(self, id, lang): """ """ return self.getLocalProperty(id, lang) security.declareProtected(view, 'getVersionProperty') def getVersionProperty(self, id): """ """ return getattr(self, id, '') # # Answer edit methods # security.declareProtected(view, 'canAddAnswerDraft') def canAddAnswerDraft(self): """ Check if current user can add an answer draft """ auth_tool = self.getAuthenticationTool() return self.allow_drafts and not auth_tool.isAnonymousUser() security.declareProtected(PERMISSION_ADD_ANSWER, 'addSurveyAnswerDraft') def addSurveyAnswerDraft(self, REQUEST=None, notify_respondent=False, **kwargs): """This is just to be able to specify submit method in zpt""" return self.addSurveyAnswer(REQUEST, notify_respondent, draft=True, **kwargs) messages_html = NaayaPageTemplateFile('zpt/survey_messages', globals(), 'NaayaSurvey.survey_messages') security.declareProtected(PERMISSION_ADD_ANSWER, 'addSurveyAnswer') def addSurveyAnswer(self, REQUEST=None, notify_respondent=False, draft=False, **kwargs): """Add someone's answer""" translate = self.getPortalI18n().get_translation if REQUEST: kwargs.update(REQUEST.form) # check survey expiration if self.expired() and not self.checkPermissionPublishObjects(): error_msg = translate("The survey has expired") if not REQUEST: raise SurveyQuestionnaireException(error_msg) self.setSessionErrorsTrans(error_msg) REQUEST.RESPONSE.redirect(self.absolute_url()) return # check datamodel datamodel = {} errors = [] if self.allow_anonymous and not self.isAnonymousUser(): anonymous_answer = kwargs.get('anonymous_answer') if anonymous_answer not in [0, 1]: errors.append( translate( 'Please specify if you want your answer to be anonymous' )) for widget in self.getWidgets(): try: value = widget.getDatamodel(kwargs) if not draft: widget.validateDatamodel(value) except WidgetError, ex: if not REQUEST: raise value = None errors.append(translate(ex.message)) datamodel[widget.getWidgetId()] = value if draft: if not self.canAddAnswerDraft(): error_msg = translate( "Can't add draft (not logged in or not allowed)") if not REQUEST: raise SurveyQuestionnaireException(error_msg) errors.append(error_msg) else: try: validation_onsubmit = self['validation_onsubmit'] except KeyError: pass else: validation_onsubmit(datamodel, errors) if not REQUEST and errors: raise WidgetError(errors[0]) # check Captcha/reCaptcha if REQUEST and not self.checkPermission(PERMISSION_SKIP_CAPTCHA): captcha_errors = self.getSite().validateCaptcha('', REQUEST) if captcha_errors: errors.append(captcha_errors) answer_id = kwargs.pop('answer_id', None) if errors: # assumed that REQUEST is not None self.setSessionErrorsTrans(errors) self.setSessionAnswer(datamodel) self.setSession('notify_respondent', notify_respondent) if answer_id is not None: answer = self._getOb(answer_id) REQUEST.RESPONSE.redirect('%s?edit=1' % answer.absolute_url()) else: REQUEST.RESPONSE.redirect(self.absolute_url()) return suggestions = [] cf_approval_list = [] if getattr(self, 'meeting_eionet_survey', None): respondent = REQUEST.get('respondent') else: respondent = None creation_date = None anonymous_editing_key = None if answer_id is not None: old_answer = self._getOb(answer_id) respondent = old_answer.respondent cf_approval_list = getattr(old_answer, 'cf_approval_list', []) suggestions = getattr(old_answer, 'suggestions', []) anonymous_editing_key = getattr(old_answer, 'anonymous_editing_key', None) if not getattr(old_answer, 'draft', False): creation_date = old_answer.get('creation_date') # an answer ID was provided explicitly for us to edit, so we # remove the old one self._delObject(answer_id) LOG('NaayaSurvey.SurveyQuestionnaire', DEBUG, 'Deleted previous answer %s while editing' % answer_id) if not self.allow_multiple_answers: # look for all old answers and remove them # (there can be more than one because of a previous bug) while True: old_answer = self.getAnswerForRespondent(respondent=respondent, all=True) if old_answer is None: break else: self._delObject(old_answer.id) LOG( 'NaayaSurvey.SurveyQuestionnaire', DEBUG, 'Deleted previous answer %s' % old_answer.absolute_url()) # If we are in edit mode, keep the answer_id from the "old answer" answer_id = manage_addSurveyAnswer(self, datamodel, REQUEST=REQUEST, draft=draft, respondent=respondent, id=answer_id, creation_date=creation_date) answer = self._getOb(answer_id) if suggestions: answer.suggestions = suggestions if cf_approval_list: answer.cf_approval_list = cf_approval_list if self.allow_anonymous and not self.isAnonymousUser(): answer.anonymous_answer = bool(anonymous_answer) if self.isAnonymousUser(): if anonymous_editing_key: answer.anonymous_editing_key = anonymous_editing_key anonymous_responder_email = kwargs.pop('anonymous_responder_email', None) if anonymous_responder_email: answer.anonymous_responder_email = anonymous_responder_email if not answer.get('anonymous_editing_key'): answer.anonymous_editing_key = self.utGenRandomId(16) self.sendNotificationToUnauthenticatedRespondent(answer) elif not draft: if (self.notify_respondents == 'ALWAYS' or (self.notify_respondents.startswith('LET_THEM_CHOOSE') and notify_respondent)): self.sendNotificationToRespondent(answer) if self.notify_owner: self.sendNotificationToOwner(answer) if REQUEST: self.delSessionKeys(datamodel.keys()) if not draft: if self.aq_parent.meta_type == 'Naaya Meeting': self.setSessionInfoTrans('Thank you for taking the survey') REQUEST.RESPONSE.redirect(self.aq_parent.absolute_url()) else: self.setSession('title', 'Thank you for taking the survey') if answer.anonymous_answer: self.setSession('body', 'You answer was recorded anonymously') self.setSession('referer', self.absolute_url()) REQUEST.RESPONSE.redirect('%s/messages_html' % self.absolute_url()) else: REQUEST.RESPONSE.redirect('%s?edit=1' % answer.absolute_url()) return answer_id
jstree.append({ 'data': { 'title': organization, 'icon': self.organization_icon }, 'children': meeting_nodes }) return json.dumps(jstree) security.declareProtected(view, 'report_meeting_participants') def report_meeting_participants(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'report_meeting_participants') security.declareProtected(view, 'report_meeting_organizations') def report_meeting_organizations(self, REQUEST=None, RESPONSE=None): """ """ return self.getFormsTool().getContent({'here': self}, 'report_meeting_organizations') # Custom page templates NaayaPageTemplateFile('zpt/report_meeting_participants', globals(), 'report_meeting_participants') NaayaPageTemplateFile('zpt/report_meeting_organizations', globals(), 'report_meeting_organizations')