class MockupAsset(Asset, SimpleItem): """A mockup non-publishable. """ grok.implements(IMockupAsset) meta_type = 'Mockup Asset' silvaconf.priority(-10) silvaconf.icon('tests/mockers.png')
class NewsItem(Document.Document): """A News item that appears as an individual page. By adjusting settings the Author can determine which subjects, and for which audiences the Article should be presented. """ grok.implements(INewsItem) security = ClassSecurityInfo() meta_type = "Obsolete Article" silvaconf.icon("www/news_item.png") silvaconf.priority(3.7) silvaconf.version_class(NewsItemVersion) security.declareProtected(SilvaPermissions.ApproveSilvaContent, 'set_next_version_display_datetime') def set_next_version_display_datetime(self, dt): """Set display datetime of next version. """ version = getattr(self, self.get_next_version()) version.set_display_datetime(dt) security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_unapproved_version_display_datetime') def set_unapproved_version_display_datetime(self, dt): """Set display datetime for unapproved """ version = getattr(self, self.get_unapproved_version()) version.set_display_datetime(dt) security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_unapproved_version_display_datetime') def get_unapproved_version_display_datetime(self): """get display datetime for unapproved """ version = getattr(self, self.get_unapproved_version()) version.get_display_datetime()
class PollQuestion(VersionedContent, ExternalSource): """This Silva extension enables users to conduct polls inside Silva sites. A Question is posed to the public and results of the answers are displayed to those that respond. The poll can be an independent page or be embedded in a document as a Code Source. """ security = ClassSecurityInfo() meta_type = 'Silva Poll Question' grok.implements(IPollQuestion, IExternalSource) silvaconf.icon("PollQuestion.png") silvaconf.version_class('PollQuestionVersion') security.declareProtected(SilvaPermissions.AccessContentsInformation, 'to_html') def to_html(self, context, request, **parameters): """Return HTMl for ExternalSource interface. """ if parameters.get('display', 'normal') == 'link': url = absoluteURL(self, request) # Should make it more sense to put the title as link ? return '<p class="p"><a href="%s">%s</a></p>' % (url, url) # Render the default view as source content. # This will change again caching headers. (XXX Messy). return getMultiAdapter((self, request), name='content.html')()
class TabBlock(Block): grok.name('tab-content') grok.title('Tabular content') grok.order(21) silvaconf.icon('tabs.png') def __init__(self): self.identifier = unicode(uuid.uuid1())
class Comment(ForumPost, Content, SimpleItem): """A comment is the smallest content of a Silva Forum, contained in a topic. """ grok.implements(IComment) silvaconf.icon('www/comment.gif') meta_type = 'Silva Forum Comment' def is_published(self): return False # always allow removal of this object from the SMI
class AgendaItem(NewsItem): """A News item for events. Includes date and location metadata, as well settings for subjects and audiences. """ security = ClassSecurityInfo() implements(IAgendaItem) meta_type = "Obsolete Agenda Item" silvaconf.icon("www/agenda_item.png") silvaconf.priority(3.8) silvaconf.versionClass(AgendaItemVersion)
class Topic(ForumContainer, ForumPost, Folder): """Topic of a Silva Forum. It will contains comments posted by users. """ grok.implements(ITopic) silvaconf.icon('www/topic.gif') meta_type = 'Silva Forum Topic' silvaconf.priority(0) security = ClassSecurityInfo() def __init__(self, *args, **kwargs): super(Topic, self).__init__(*args, **kwargs) self._lastid = 0 self.comment_batch_size = 10 def get_silva_addables_allowed_in_container(self): return ['Silva Forum Comment'] security.declareProtected('Change Silva content', 'add_comment') def add_comment(self, title, text, anonymous=False): """ add a comment to the topic """ if anonymous and not self.anonymous_posting_allowed(): raise ValueError('anonymous posting is not allowed!') idstring = title if not idstring: idstring = text identifier = self._generate_id(idstring) factory = self.manage_addProduct['SilvaForum'] factory.manage_addComment(identifier, title, text=text) comment = self[identifier] if anonymous: binding = self.get_root().service_metadata.getMetadata(comment) binding.setValues('silvaforum-item', {'anonymous': 'yes'}) return comment def comments(self): """ returns an iterable of all comments """ return [{ 'id': comment.id, 'url': comment.absolute_url(), 'title': comment.get_title(), 'creator': comment.get_creator(), 'creation_datetime': comment.get_creation_datetime(), 'text': comment.get_text(), 'topic_url': comment.aq_parent.absolute_url(), } for comment in self.objectValues('Silva Forum Comment')] security.declareProtected('View', 'number_of_comments') def number_of_comments(self): return len(self.objectValues('Silva Forum Comment'))
class Link(VersionedContent): __doc__ = _("""A Silva Link makes it possible to create links that show up in navigation or a Table of Contents. The links can be absolute or relative. Absolute links go to external sites while relative links go to content within Silva. """) meta_type = "Silva Link" grok.implements(interfaces.ILink) silvaconf.icon('icons/link.png') silvaconf.versionClass('LinkVersion')
class SilvaSoftwareCenter(Publication): meta_type = 'Silva Software Center' grok.implements(interfaces.ISilvaSoftwareCenter) silvaconf.icon('SilvaSoftwareCenter.png') silvaconf.priority(9) def get_silva_addables_allowed_in_container(self): return [ 'Silva Software Group', 'Silva Software Activity Aggregator', 'Silva Software Remote Group', 'Silva Software Package', ]
class Ghost(VersionedContent): __doc__ = _("""Ghosts are special documents that function as a placeholder for an item in another location (like an alias, symbolic link, shortcut). Unlike a hyperlink, which takes the Visitor to another location, a ghost object keeps the Visitor in the current publication, and presents the content of the ghosted item. The ghost inherits properties from its location (e.g. layout and stylesheets). """) meta_type = "Silva Ghost" security = ClassSecurityInfo() grok.implements(IGhost) silvaconf.icon('icons/ghost.png') silvaconf.version_class(GhostVersion) security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_haunted') def get_haunted(self): version = self.get_previewable() if version is not None: return version.get_haunted() return None security.declareProtected(SilvaPermissions.AccessContentsInformation, 'is_published') def is_published(self): public = self.get_viewable() if public is None: return False haunted = public.get_haunted() if haunted is None: return False return haunted.is_published() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_modification_datetime') def get_modification_datetime(self): """Return modification datetime. """ version = self.get_viewable() if version is not None: content = version.get_haunted() if content is not None: return content.get_modification_datetime() return super(Ghost, self).get_modification_datetime()
class ServicePolls(SilvaService): """Service that manages poll data """ grok.implements(IServicePolls) meta_type = 'Silva Poll Service' default_service_identifier = 'service_polls' silvaconf.icon('ServicePolls.png') manage_options = ({ 'label': 'Settings', 'action': 'manage_settings' }, ) + SilvaService.manage_options security = ClassSecurityInfo() _store_cookies = True _automatically_hide_question = True def _init_database(self): self.db = DB() def create_question(self, question, answers): return self.db.create(question, answers) def get_question(self, qid): return self.db.get(qid).question def set_question(self, qid, question): return self.db.set_question(qid, question) def get_answers(self, qid): return self.db.get(qid).answers def set_answers(self, qid, answers): return self.db.set_answers(qid, answers) def get_votes(self, qid): return self.db.get(qid).votes def vote(self, qid, index): self.db.vote(qid, index) def automatically_hide_question(self): return self._automatically_hide_question def store_cookies(self): return self._store_cookies
class SilvaSoftwareGroup(SilvaSoftwareContent): meta_type = 'Silva Software Group' grok.implements(interfaces.ISilvaSoftwareGroup) silvaconf.icon('SilvaSoftwareGroup.png') silvaconf.priority(8) group_tag = u"" is_group_archive = False def get_silva_addables_allowed_in_container(self): return [ 'Silva Document', 'Silva Link', 'Silva Software Activity Aggregator', 'Silva Software Group', 'Silva Software Package', ]
class SilvaSoftwarePackage(SilvaSoftwareContent): """A package represent a software and contains releases. """ meta_type = 'Silva Software Package' grok.implements(interfaces.ISilvaSoftwarePackage) silvaconf.icon('SilvaSoftwarePackage.png') silvaconf.priority(9) is_package_deprecated = False package_version_matrix = u"" def get_silva_addables_allowed_in_container(self): result = [ 'Silva Document', 'Silva Software Release', 'Silva Software Activity' ] result.extend(IAddableContents(self).get_all_addables(IAsset)) return result
class FilesService(SilvaService): meta_type = 'Silva Files Service' grok.implements(IFilesService, ISilvaConfigurableService) grok.name('service_files') silvaconf.default_service() silvaconf.icon('icons/service_files.png') security = ClassSecurityInfo() storage = FieldProperty(IFilesService['storage']) manage_options = ({ 'label': 'Settings', 'action': 'manage_settings' }, ) + SilvaService.manage_options security.declarePrivate('new_file') def new_file(self, id): if self.storage is None: return ZODBFile(id) return self.storage(id) security.declarePrivate('upgrade_storage') def convert_storage(self, container): if self.storage is not None and self.storage is not ZODBFile: upg = upgrade.UpgradeRegistry() upg.register(StorageConverterHelper(container), '0.1', upgrade.AnyMetaType) upg.register(FileStorageConverter(self), '0.1', 'Silva File') upg.register(ImageStorageConverter(self), '0.1', 'Silva Image') upg.upgrade_tree(container, '0.1') def is_file_using_correct_storage(self, content): storage = ZODBFile if self.storage is not None: storage = self.storage return isinstance(content, storage)
class MockupVersionedContent(VersionedContent): """Test versioned content. (Note: the docstring is required for traversing to work) """ meta_type = 'Mockup VersionedContent' grok.implements(IMockupVersionedContent) silvaconf.priority(-11) silvaconf.version_class(MockupVersion) silvaconf.icon('tests/mockers.png') def __init__(self, *args): super(MockupVersionedContent, self).__init__(*args) self.__entries = [] def set_entries(self, entries): self.__entries = entries def get_entries(self): return list(self.__entries) def get_mockup_version(self, version_id): return self._getOb(str(version_id))
class CSVSource(Folder, Asset, EditableExternalSource): """CSV Source is an asset that displays tabular data from a spreadsheet or database. The format of the uploaded text file should be ‘comma separated values’. The asset can be linked directly, or inserted in a document with the External Source element. If necessary, all aspects of the display can be customized in the rendering templates of the CSV Source. """ grok.implements(ICSVSource) meta_type = "Silva CSV Source" security = ClassSecurityInfo() _layout_id = 'layout' _default_batch_size = 20 # register priority, icon and factory silvaconf.priority(1) silvaconf.icon('www/csvsource.png') def __init__(self, id): super(CSVSource, self).__init__(id) self._raw_data = '' self._data = [] # ACCESSORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'to_html') def to_html(self, content, request, **parameters): """ render HTML for CSV source """ rows = self._data[:] param = {} param.update(parameters) if not param.get('csvtableclass'): param['csvtableclass'] = 'default' batch_size = self._default_batch_size batch = '' if param.get('csvbatchsize'): batch_size = int(param.get('csvbatchsize')) model = content if IVersion.providedBy(content): model = content.get_content() if rows: headings = rows[0] rows = Batch(rows[1:], count=batch_size, name=self.getId(), request=request) param['headings'] = headings batch = getMultiAdapter((model, rows, request), IBatching)() return self.layout(table=rows, batch=batch, parameters=param) security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_file') def get_file(self): """Return the file content. """ return self._raw_data security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_file_size') def get_file_size(self): """Get the size of the file as it will be downloaded. """ if self._raw_data: return len(self._raw_data) return 0 security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_mime_type') def get_mime_type(self): return 'text/csv' security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_filename') def get_filename(self): return self.getId() + '.csv' security.declareProtected(SilvaPermissions.ViewManagementScreens, 'get_table_class') def get_table_class(self): """Returns css class for table """ return self._table_class security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_description') def get_description(self): """ Return desc from meta-data system""" ms = self.service_metadata return ms.getMetadataValue(self, 'silva-extra', 'content_description') # MODIFIERS def _update_data(self, data): def convert_to_unicode(line): return [v.decode(self._data_encoding, 'replace') for v in line] try: csv_data = list(map(convert_to_unicode, csv.reader(StringIO(data)))) except csv.Error as error: raise ValueError("Invalid CSV file: %s" % error.args[0]) self._data = csv_data self._raw_data = data notify(ObjectModifiedEvent(self)) security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_file') def set_file(self, file): return self._update_data(file.read()) security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_data_encoding') def set_data_encoding(self, encoding): self._data_encoding = encoding self._update_data(self._raw_data) security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_table_class') def set_table_class(self, css_class): self._table_class = css_class security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_description') def set_description(self, desc): if not isinstance(desc, str): desc = desc.encode('utf-8') binding = getUtility(IMetadataService).getMetadata(self) binding.setValues('silva-extra', {'content_description': desc})
class EmailMessageService(SilvaService): """Simple implementation of IMemberMessageService that sends email messages. """ meta_type = 'Silva Message Service' grok.implements(interfaces.IMessageService, ISilvaConfigurableService) grok.name('service_messages') silvaconf.default_service() silvaconf.icon('icons/service_message.png') manage_options = ( {'label':'Settings', 'action':'manage_settings'}, ) + SilvaService.manage_options security = ClassSecurityInfo() _fromaddr = None _enabled = False # XXX these security settings are not the right thing.. perhaps # create a new permission? security.declareProtected( SilvaPermissions.ChangeSilvaAccess, 'send_message') def send_message(self, from_memberid, to_memberid, subject, message): email_queue.enqueue_email( from_memberid, to_memberid, subject, message) security.declarePublic('send_pending_messages') def send_pending_messages(self): logger.debug("Sending pending messages...") service_members = getUtility(IMemberService) get_member = service_members.get_member for to_memberid, message_dict in email_queue: to_member = get_member(to_memberid) if to_member is None: # XXX get_member should return a NoneMember, not just None # in case the member cannot be found. Apparently sometimes # it *does* return. logger.debug("no member found for: %s" % to_memberid) continue to_email = to_member.email() if to_email is None: logger.debug("no email for: %s" % to_memberid) continue lines = [] # XXX usually all messages have the same subject yet, # but this can be assumed here per se. common_subject=None reply_to = {} for from_memberid, messages in message_dict.items(): logger.debug("From memberid: %s " % from_memberid) from_member = get_member(from_memberid) if from_member is None: # XXX get_member should return a NoneMember, not just None # in case the member cannot be found. Apparently sometimes # it *does* return. logger.debug("no member found for: %s" % to_memberid) continue from_email = from_member.email() if from_email is not None: reply_to[from_email] = 1 lines.append("Message from: %s (email: %s)" % (from_memberid, from_email)) else: lines.append("Message from: %s (no email available)" % from_memberid) for subject, message in messages: lines.append(subject) lines.append('') lines.append(message) lines.append('') if common_subject is None: common_subject = subject else: if common_subject != subject: # XXX this is very stupid, but what else? # maybe leave empty? common_subject = 'Notification on status change' text = '\n'.join(lines) header = {} if common_subject is not None: header['Subject'] = common_subject if reply_to: header['Reply-To'] = ', '.join(reply_to.keys()) # XXX set from header ? self._send_email(to_email, text, header=header) # XXX if above raises exception: mail queue is not flushed # as this line is not reached. bug or feature ? email_queue.clear() # ACCESSORS security.declareProtected(SilvaPermissions.ViewManagementScreens, 'fromaddr') def fromaddr(self): """return self._fromaddr""" return self._fromaddr security.declareProtected(SilvaPermissions.ViewManagementScreens, 'send_email_enabled') def send_email_enabled(self): return self._enabled def _send_email(self, toaddr, msg, header={}): if not self._enabled: return header['To'] = toaddr if not header.has_key('From'): header['From'] = self._fromaddr if not header.has_key('Sender'): header['Sender'] = self._fromaddr header['Content-Type'] = 'text/plain; charset=UTF-8' msg_lines = [ '%s: %s' % (k, v) for k, v in header.items() ] msg_lines.append('') msg_lines.append(msg) msg = '\r\n'.join(msg_lines) if isinstance(msg, unicode): msg = msg.encode('UTF-8') # Send the email using the mailhost sendmail(self, msg, toaddr, self._fromaddr)
class ExtensionService(SilvaService, Folder): meta_type = 'Silva Extension Service' grok.implements(IExtensionService, ISilvaConfigurableService) grok.name('service_extensions') silvaconf.default_service() silvaconf.icon('icons/service_extension.png') security = ClassSecurityInfo() manage_options = ( { 'label': 'Extensions', 'action': 'manage_extensions' }, { 'label': 'Partial upgrades', 'action': 'manage_partialUpgrade' }, { 'label': 'Partial reindex', 'action': 'manage_partialReindex' }, { 'label': 'Logs', 'action': 'manage_main' }, ) + SilvaService.manage_options _site_quota = 0 _quota_enabled = False _quota_verify = False # MANIPULATORS security.declareProtected('View management screens', 'install') def install(self, name): """Install extension """ root = self.get_root() extensionRegistry.install(name, root) security.declareProtected('View management screens', 'uninstall') def uninstall(self, name): """Uninstall extension """ root = self.get_root() extensionRegistry.uninstall(name, root) security.declareProtected('View management screens', 'refresh') def refresh(self, name): """Refresh extension. """ root = self.get_root() extensionRegistry.refresh(name, root) security.declareProtected('View management screens', 'refresh_all') def refresh_all(self): """Refreshes all extensions """ for name in extensionRegistry.get_names(): if self.is_installed(name): self.refresh(name) security.declareProtected('View management screens', 'reindex_all') def reindex_all(self): """Refresh the silva catalog. """ root = self.get_root() root.service_catalog.manage_catalogClear() logger.info('Catalog cleared.') index_content(root) security.declareProtected('View management screens', 'reindex_subtree') def reindex_subtree(self, path): """reindexes a subtree. """ root = self.get_root() index_content(root.unrestrictedTraverse(str(path)), reindex=True) security.declareProtected('View management screens', 'disable_quota_subsystem') def disable_quota_subsystem(self): """Disable quota sub-system. """ if not self._quota_enabled: return False if self._site_quota: # You cannot disable the quota system if there is a site quota. return False root = self.get_root() # Disable metadata for quota collection = root.service_metadata.getCollection() if 'silva-quota' in collection.objectIds(): collection.manage_delObjects(['silva-quota']) setids = ('silva-quota', ) types = ( 'Silva Root', 'Silva Publication', ) root.service_metadata.removeTypesMapping(types, setids) self._quota_enabled = False self._quota_verify = False return True security.declareProtected('View management screens', 'enable_quota_subsystem') def enable_quota_subsystem(self): """Enable quota sub-system. """ if self._quota_enabled: return False root = self.get_root() # Setup metadata for quota schema = os.path.join(os.path.dirname(__file__), 'schema') collection = root.service_metadata.getCollection() if 'silva-quota' in collection.objectIds(): collection.manage_delObjects(['silva-quota']) xml_file = os.path.join(schema, 'silva-quota.xml') with open(xml_file, 'r') as fh: collection.importSet(fh) setids = ('silva-quota', ) types = [ c['name'] for c in extensionRegistry.get_contents(requires=[IPublication]) ] root.service_metadata.addTypesMapping(types, setids) root.service_metadata.initializeMetadata() root.used_space = compute_used_space(root) self._quota_enabled = True self._quota_verify = True return True security.declareProtected('View management screens', 'upgrade_content') def upgrade_content(self, content, from_version, to_version): """Upgrade the given content """ now = datetime.now().strftime('%Y-%b-%dT%H%M%S') log_filename = 'upgrade-log-%s-to-%s-on-%s.log' % (from_version, to_version, now) log = upgrade.registry.upgrade(content, from_version, to_version) factory = self.manage_addProduct['OFS'] factory = factory.manage_addFile(log_filename, log.read(), content_type='text/plain') if interfaces.IRoot.providedBy(content): content._content_version = to_version # ACCESSORS security.declareProtected('Access contents information', 'get_quota_subsystem_status') def get_quota_subsystem_status(self): if not self._quota_enabled: return None return self._quota_verify security.declareProtected('Access contents information', 'get_site_quota') def get_site_quota(self): return self._site_quota security.declareProtected('Access contents information', 'is_installed') def is_installed(self, name): """Is extension installed? """ root = self.get_root() return extensionRegistry.is_installed(name, root)
class Image(Asset): __doc__ = _("""Web graphics (gif, jpg, png) can be uploaded and inserted in documents, or used as viewable assets. """) security = ClassSecurityInfo() meta_type = "Silva Image" grok.implements(interfaces.IImage) re_WidthXHeight = re.compile(r'^([0-9]+|\*)[Xx]([0-9\*]+|\*)$') re_percentage = re.compile(r'^([0-9\.]+)\%$') re_box = re.compile(r'^([0-9]+)[Xx]([0-9]+)-([0-9]+)[Xx]([0-9]+)') thumbnail_size = Size(120, 120) image = None hires_image = None thumbnail_image = None web_scale = '100%' web_crop = '' web_format = Format.JPEG web_formats = (Format.JPEG, Format.GIF, Format.PNG) _web2ct = { Format.JPEG: 'image/jpeg', Format.GIF: 'image/gif', Format.PNG: 'image/png', } silvaconf.priority(-3) silvaconf.icon('icons/image.gif') silvaconf.factory('manage_addImage') security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_web_presentation_properties') def set_web_presentation_properties(self, web_format, web_scale, web_crop): """Sets format and scaling for web presentation. web_format (str): either JPEG or PNG (or whatever other format makes sense, must be recognised by PIL). web_scale (str): WidthXHeight or nn.n%. web_crop (str): X1xY1-X2xY2, crop-box or empty for no cropping. Automaticaly updates cached web presentation image. """ update = False if self.hires_image is None: update = True self.hires_image = self.image self.image = None # Set web format. if web_format not in ('unknown', '') and self.web_format != web_format: if web_format in self.web_formats: self.web_format = web_format update = True else: raise ValueError('Unknown image format %s' % web_format) # check if web_scale can be parsed: try: self.get_canonical_web_scale(web_scale) except ValueError: # if not, we set web_scale back to default value web_scale = '100%' if self.web_scale != web_scale: self.web_scale = web_scale update = True # check if web_crop can be parsed: self.get_crop_box(web_crop) if self.web_crop != web_crop: # if web_crop is None it should be replaced by an empty string self.web_crop = web_crop and web_crop or '' update = True if update and self.hires_image is not None: self._create_derived_images() security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_image') def set_image(self, file): """Set the image object. """ validate_image(file) self._image_factory('hires_image', file) # Image change, reset scale, crop box: they can be invalid for # this new image. format = self.get_format() if format in self.web_formats: self.web_format = format self.web_scale = '100%' self.web_crop = '' self._create_derived_images() # XXX Should be on event self.update_quota() security.declareProtected(SilvaPermissions.View, 'get_image') def get_image(self, hires=True, webformat=False): """Return image data. """ if hires: if self.hires_image is not None: if webformat: # Create web format of original image. with ImageFile(self.hires_image) as working: data = working.save(self.web_format) if data is not None: return data.getvalue() # Original format of the original image is the orginal. return self.hires_image.get_file() return None if self.image is not None: if webformat: # Webformat of the cropped/resized image is already computed. return self.image.get_file() # Original format of the cropped/resize image is not possible. raise ValueError( _(u"Low resolution image in original format is " u"not supported")) return None security.declareProtected(SilvaPermissions.View, 'get_canonical_web_scale') def get_canonical_web_scale(self, scale=None): """returns (width, height) of web image""" if scale is None: scale = self.web_scale m = self.re_WidthXHeight.match(scale) if m is None: m = self.re_percentage.match(scale) if m is None: msg = _( "'${scale}' is not a valid scale identifier. " "Probably a percent symbol is missing.", mapping={'scale': scale}) raise ValueError(msg) cropbox = Rect.parse(self.web_crop) if cropbox: width, height = cropbox.size else: width, height = self.get_dimensions() percentage = float(m.group(1)) / 100.0 width = int(width * percentage) height = int(height * percentage) else: img_w, img_h = self.get_dimensions() width = m.group(1) height = m.group(2) if width == height == '*': msg = _( "'${scale} is not a valid scale identifier. " "At least one number is required.", mapping={'scale': scale}) raise ValueError(msg) if width == '*': height = int(height) width = img_w * height / img_h elif height == '*': width = int(width) height = img_h * width / img_w else: width = int(width) return width, height security.declareProtected(SilvaPermissions.View, 'get_crop_box') def get_crop_box(self, crop=None): """return crop box""" crop = crop or self.web_crop if crop is None or crop.strip() == '': return None rect = Rect.parse(crop) if rect is None: msg = _("'${crop} is not a valid crop identifier", mapping={'crop': crop}) raise ValueError(msg) with ImageFile(self.hires_image) as image: Crop(rect).validate(image) return (rect.lower_edge.x, rect.lower_edge.y, rect.higher_edge.x, rect.higher_edge.y) security.declareProtected(SilvaPermissions.View, 'get_dimensions') def get_dimensions(self, thumbnail=False, hires=False): """Returns width, heigt of (hi res) image. Raises ValueError if there is no way of determining the dimenstions, Return 0, 0 if there is no image, Returns width, height otherwise. """ data = None if thumbnail: data = self.thumbnail_image elif hires: data = self.hires_image else: data = self.image if data is None: return Size(0, 0) try: with ImageFile(data) as image: return image.get_size() except (ValueError, TypeError): return Size(0, 0) security.declareProtected(SilvaPermissions.View, 'tag') def tag(self, hires=False, thumbnail=False, request=None, preview=False, **extra_attributes): warnings.warn( 'tag have been replaced with get_html_tag. ' 'It will be removed, please update your code.', DeprecationWarning, stacklevel=2) return self.get_html_tag(hires=hires, thumbnail=thumbnail, request=request, preview=preview, **extra_attributes) security.declareProtected(SilvaPermissions.View, 'get_html_tag') def get_html_tag(self, preview=False, request=None, hires=False, thumbnail=False, **extra_attributes): """ return xhtml tag Since 'class' is a Python reserved word, it cannot be passed in directly in keyword arguments which is a problem if you are trying to use 'tag()' to include a CSS class. The tag() method will accept a 'css_class' argument that will be converted to 'class' in the output tag to work around this. """ url = self.get_download_url(request=request, preview=preview, hires=hires, thumbnail=thumbnail) title = self.get_title_or_id() width, height = self.get_dimensions(thumbnail=thumbnail, hires=hires) if extra_attributes.has_key('css_class'): extra_attributes['class'] = extra_attributes['css_class'] del extra_attributes['css_class'] extra_html_attributes = [ u'{name}="{value}"'.format(name=escape(name, 1), value=escape(value, 1)) for name, value in extra_attributes.iteritems() ] return u'<img src="{src}" width="{width}" height="{height}" ' \ u'alt="{alt}" {extra_attributes} />'.format( src=url, width=str(width), height=str(height), alt=escape(title, 1), extra_attributes=u" ".join(extra_html_attributes)) security.declareProtected(SilvaPermissions.View, 'url') def url(self, hires=False, thumbnail=False, request=None, preview=False): warnings.warn( 'url have been replaced with get_download_url. ' 'It will be removed, please update your code.', DeprecationWarning, stacklevel=2) return self.get_download_url(hires=hires, thumbnail=thumbnail, request=request, preview=preview) security.declareProtected(SilvaPermissions.View, 'get_download_url') def get_download_url(self, preview=False, request=None, hires=False, thumbnail=False): "return url of image" if request is None: request = self.REQUEST url = getMultiAdapter((self, request), IContentURL).url(preview=preview) more = '?' if hires: url += '?hires' more = '&' elif thumbnail: url += '?thumbnail' more = '&' if preview: # In case of preview we add something that change at the # end of the url to prevent caching from the browser. url += more + str(int(time.time())) return url security.declareProtected(SilvaPermissions.View, 'get_web_format') def get_web_format(self): """Return file format of web presentation image """ try: with ImageFile(self.image) as image: return image.get_format() except (ValueError, TypeError): return 'unknown' security.declareProtected(SilvaPermissions.View, 'get_web_scale') def get_web_scale(self): """Return scale percentage / WxH of web presentation image """ return str(self.web_scale) security.declareProtected(SilvaPermissions.View, 'get_web_crop') def get_web_crop(self): """Return crop identifier """ return str(self.web_crop) security.declareProtected(SilvaPermissions.View, 'get_orientation') def get_orientation(self): """Returns translated Image orientation (string). """ width, height = self.get_dimensions() if width == height: return _("square") elif width > height: return _("landscape") return _("portrait") security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'get_file_system_path') def get_file_system_path(self): """return path on filesystem for containing image""" if self.hires_image is not None: return self.hires_image.get_file_system_path() return None security.declareProtected(SilvaPermissions.View, 'get_format') def get_format(self): """Returns image format. """ try: with ImageFile(self.hires_image) as image: return image.get_format() except (ValueError, TypeError): return 'unknown' security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_filename') def get_filename(self): if self.hires_image is None: return self.getId() return self.hires_image.get_filename() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_mime_type') def get_mime_type(self): if self.hires_image is None: return 'application/octet-stream' return self.hires_image.get_mime_type() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_content_type') def get_content_type(self): if self.hires_image is None: return 'application/octet-stream' return self.hires_image.get_content_type() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_file_size') def get_file_size(self): if self.hires_image is None: return 0 return self.hires_image.get_file_size() ########## ## private def _create_derived_images(self): self._create_web_presentation() self._create_thumbnail() def _create_web_presentation(self): try: transformer = Transformer() cropbox = self.get_crop_box() if cropbox is not None: crop_rect = Rect.from_points(Point(cropbox[0], cropbox[1]), Point(cropbox[2], cropbox[3])) transformer.append(Crop(crop_rect)) if self.web_scale != '100%': spec = WHResizeSpec.parse(self.web_scale) if spec is None: spec = PercentResizeSpec.parse(self.web_scale) if spec is not None: transformer.append(Resize(spec)) image_io = transformer.transform(self.hires_image, self.web_format) if image_io: content_type = self._web2ct[self.web_format] self._image_factory('image', image_io, content_type) else: self.image = self.hires_image except IOError as error: logger.error("Web presentation creation failed for %s with %s" % ('/'.join(self.getPhysicalPath()), str(error))) if str(error.args[0]) == "cannot read interlaced PNG files": self.image = self.hires_image return raise ValueError(str(error)) except ValueError as error: logger.error("Web presentation creation failed for %s with %s" % ('/'.join(self.getPhysicalPath()), str(error))) self.image = self.hires_image return def _create_thumbnail(self): try: transformer = Transformer(ThumbnailResize(self.thumbnail_size)) thumb = transformer.transform(self.image or self.hires_image, self.web_format) if thumb: content_type = self._web2ct[self.web_format] self._image_factory('thumbnail_image', thumb, content_type) except IOError as error: logger.info("Thumbnail creation failed for %s with %s" % ('/'.join(self.getPhysicalPath()), str(error))) if str(error.args[0]) == "cannot read interlaced PNG files": self.thumbnail_image = None return else: raise ValueError(str(error)) except ValueError, e: logger.info("Thumbnail creation failed for %s with %s" % ('/'.join(self.getPhysicalPath()), str(e))) # no thumbnail self.thumbnail_image = None return
class CodeSource(EditableExternalSource, Folder, ZMIObject): grok.implements(ICodeSource) # register icon and factories silvaconf.icon('www/codesource.png') silvaconf.factory('manage_addCodeSourceForm') silvaconf.factory('manage_addCodeSource') silvaconf.zmi_addable(True) meta_type = "Silva Code Source" security = ClassSecurityInfo() _data_encoding = 'UTF-8' _fs_location = None _script_layers = [] # ZMI Tabs manage_options = ( { 'label': 'Edit', 'action': 'editCodeSource' }, { 'label': 'Parameters', 'action': 'parameters/manage_main' }, ) + Folder.manage_options management_page_charset = 'utf-8' security.declareProtected(SilvaPermissions.ViewManagementScreens, 'editCodeSource') editCodeSource = PageTemplateFile('www/codeSourceEdit', globals(), __name__='editCodeSource') def __init__(self, id, script_id=None, fs_location=None): super(CodeSource, self).__init__(id) self._script_id = script_id self._fs_location = fs_location security.declareProtected(SilvaPermissions.ViewManagementScreens, 'test_source') def test_source(self): # return a list of problems or None errors = [] # in real life the parent of the form is the document. We try # to do the same here. root = self.get_root() if root.get_default(): root = root.get_default() if self.parameters is not None: try: self.parameters.test_form( context=root, bad_fields=['context', 'content', 'model', 'script']) except ValueError as error: errors.extend(error.args) if not self.title: errors.append(u'Missing required source title.') if not self._script_id: errors.append(u'Missing required renderer id.') else: ids = self.objectIds() scripts = [self._script_id] + map(itemgetter(0), self._script_layers) for script_id in scripts: if script_id not in ids: errors.append( u'Missing renderer %s. Please a script or template with this id.' % (script_id)) if errors: return errors return None security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_icon') def get_icon(self): return self._getOb('icon.png', None) # ACCESSORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_script_id') def get_script_id(self): return self._script_id security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_script_layers') def get_script_layers(self): result = [] skin = grok.skin.bind(default=lambda l: l.__identifier__) for script_id, layer in self._script_layers: result.append(":".join((script_id, skin.get(layer)))) return '\n'.join(result) security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_fs_location') def get_fs_location(self): return self._fs_location security.declareProtected(SilvaPermissions.AccessContentsInformation, 'to_html') def to_html(self, content, request, **parameters): """Render HTML for code source """ script = None if self._script_layers: # If there are script_layer, check them first. for script_id, layer in self._script_layers: if layer.providedBy(request): break else: # No matching layer, default. script_id = self._script_id else: # No script_layer, default one. script_id = self._script_id if script_id is not None: script = self._getOb(script_id, None) if script_id is None or script is None: # Missing script return None parameters['REQUEST'] = request if IVersion.providedBy(content): parameters['version'] = content parameters['model'] = content.get_silva_object() else: parameters['version'] = None parameters['model'] = content __traceback_supplement__ = (CodeSourceErrorSupplement, self, parameters) result = script(**parameters) if isinstance(result, unicode): return result return unicode(result, self.get_data_encoding(), 'replace') # MANAGERS security.declareProtected(SilvaPermissions.ViewManagementScreens, 'set_script_id') def set_script_id(self, script_id): self._script_id = script_id security.declareProtected(SilvaPermissions.ViewManagementScreens, 'set_script_layers') def set_script_layers(self, script_layers): found = [] for lineno, line in enumerate(script_layers.strip().splitlines()): entries = line.strip().split(':', 1) if len(entries) != 2: raise ValueError( u'Invalid script layers: invalid form on line %d' % (lineno)) script_id, layer_identifier = entries layer = queryUtility(IBrowserSkinType, name=layer_identifier) if layer is None: raise ValueError( u'Invalid script layer: layer %s not found on line %d' % (layer_identifier, lineno)) found.append((script_id, layer)) self._script_layers = found def _get_installable(self, location=None): """Return the installable source associated with this code source. """ if location is None: location = self.get_fs_location() if location is None: return None service = queryUtility(ICodeSourceService) if service is None: # XXX pre-migration Silva 3.0 service = self.service_codesources candidates = list(service.get_installable_source(location=location)) if len(candidates) == 1: return candidates[0] return None security.declareProtected(SilvaPermissions.ViewManagementScreens, 'manage_getFileSystemLocations') def manage_getFileSystemLocations(self): service = queryUtility(ICodeSourceService) if service is None: # XXX pre-migration Silva 3.0 service = self.service_codesources return map(lambda source: source.location, service.get_installable_source(identifier=self.id)) security.declareProtected(SilvaPermissions.ViewManagementScreens, 'manage_updateCodeSource') def manage_updateCodeSource(self, purge=False, REQUEST=None): """Update a code source from the filesystem. """ installable = self._get_installable() if (installable is None or not os.path.isdir(installable._directory)): if REQUEST is not None: return self.editCodeSource( manage_tabs_message=\ 'Couldn\'t find the code source on the filesystem.') return False installable.update(self, bool(purge)) if REQUEST is not None: return self.editCodeSource( manage_tabs_message='Source updated from the filesystem.') return True security.declareProtected(SilvaPermissions.ViewManagementScreens, 'manage_exportCodeSource') def manage_exportCodeSource(self, aszip=False, dump=False, REQUEST=None): """Export a code source to the filesystem. """ if dump: installable = self._get_installable() if (installable is None or not os.path.isdir(installable._directory)): message = "Couldn't find the code source on the filesystem." else: installable.export(self) message = "Source dumped to the filesystem." if REQUEST is not None: return self.editCodeSource(manage_tabs_message=message) return None directory = tempfile.mkdtemp('-codesource-export') try: CodeSourceExportable().export(self, directory) result = io.BytesIO() archive = zipfile.ZipFile(result, 'w') for path, directories, filenames in os.walk(directory): root = path[len(directory):] if root: root = os.path.join(self.getId(), root) else: root = self.getId() for filename in filenames: archive.write(os.path.join(path, filename), os.path.join(root, filename)) archive.close() finally: shutil.rmtree(directory) if REQUEST is not None: REQUEST.RESPONSE.setHeader('Content-Type', 'application/zip') REQUEST.RESPONSE.setHeader( 'Content-Disposition', 'attachment;filename=%s.zip' % self.getId()) return result.getvalue() security.declareProtected(SilvaPermissions.ViewManagementScreens, 'manage_editCodeSource') def manage_editCodeSource(self, title, script_id, data_encoding, description=None, location=None, cacheable=None, previewable=None, usable=None, script_layers=None): """ Edit a code source settings. """ msg = u'' if location is not None and location != self._fs_location: if location: installable = self._get_installable(location) if installable is None: msg += "Invalid location for the code source " + \ "definition, not changed! " return self.editCodeSource(manage_tabs_message=msg) self._fs_location = location msg += "Code source location changed. " if data_encoding != self._data_encoding: try: unicode('abcd', data_encoding, 'replace') except LookupError: # unknown encoding, return error message msg += "Unknown encoding %s, not changed! " % data_encoding return self.editCodeSource(manage_tabs_message=msg) self.set_data_encoding(data_encoding) msg += u'Data encoding changed. ' if script_layers is not None: try: self.set_script_layers(script_layers) except ValueError as error: msg += "Error while setting script layers: %s" % error.args[0] return self.editCodeSource(manage_tabs_message=msg) title = unicode(title, self.management_page_charset) if title and title != self.title: self.title = title msg += "Title changed. " if script_id and script_id != self._script_id: self._script_id = script_id msg += "Script id changed. " # Assume description is in the encoding as specified # by "management_page_charset". Store it in unicode. if description is not None: description = unicode(description, self.management_page_charset) if description != self._description: self.set_description(description) msg += "Description changed. " if not bool(cacheable) is self.is_cacheable(): self.set_cacheable(bool(cacheable)) msg += "Cacheability setting changed. " if not bool(usable) is self.is_usable(): self.set_usable(bool(usable)) msg += "Usability setting changed. " if not bool(previewable) is self.is_previewable(): self.set_previewable(bool(previewable)) msg += "Previewable setting changed. " return self.editCodeSource(manage_tabs_message=msg)
class SimpleMemberService(SilvaService): grok.implements(IMemberService) grok.baseclass() silvaconf.icon('icons/service_member.png') security = ClassSecurityInfo() meta_type = 'Silva Simple Member Service' # XXX will be used by access tab and should be opened wider if this # is central service.. security.declareProtected(SilvaPermissions.ApproveSilvaContent, 'find_members') def find_members(self, search_string, location=None): if len(search_string) < 2: raise MemberLookupError( _(u"The search input is too short. " u"Please enter two or more characters.")) # XXX: get_valid_userids is evil: will break with other user # folder implementations. userids = self.get_valid_userids() result = [] for userid in userids: if userid.find(search_string) != -1: result.append(self.get_cached_member(userid)) return result security.declareProtected( SilvaPermissions.AccessContentsInformation, 'is_user') def is_user(self, userid, location=None): # XXX: get_valid_userids is evil: will break with other user # folder implementations. if location is None: location = self return userid in location.get_valid_userids() security.declareProtected( SilvaPermissions.AccessContentsInformation, 'get_member') def get_member(self, userid, location=None): if not self.is_user(userid, location=location): return None # get member, add it if it doesn't exist yet members = self.get_root()._getOb('Members') member = members._getOb(userid, None) if member is None: members.manage_addProduct['Silva'].manage_addSimpleMember(userid) member = members._getOb(userid) return member def get_display_usernames(self): return False def get_display_emails(self): return False def get_redirect_to_root(self): return False security.declareProtected( SilvaPermissions.AccessContentsInformation, 'get_cached_member') def get_cached_member(self, userid, location=None): """Returns a cloned member object, which can be stored in the ZODB""" return cloneMember(self.get_member(userid, location=location)).__of__(self) security.declarePublic('logout') def logout(self, came_from=None, REQUEST=None): """Logout the user. """ if REQUEST is None and hasattr(self, REQUEST): REQUEST = self.REQUEST if REQUEST is None: return # URL2 removes service_members/logout REQUEST.RESPONSE.redirect(REQUEST.URL2 + '/edit/logout')
class File(Asset): __doc__ = """Any digital file can be uploaded as Silva content. For instance large files such as pdf docs or mpegs can be placed in a site. File objects have metadata as well.""" security = ClassSecurityInfo() meta_type = "Silva File" grok.implements(interfaces.IFile) silvaconf.icon('icons/file.png') silvaconf.factory('manage_addFile') # Default values _filename = None _content_encoding = None # ACCESSORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_filename') def get_filename(self): """Object's id is filename if not set. """ if self._filename is not None: return self._filename return self.id security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_file_size') def get_file_size(self): """Get the size of the file as it will be downloaded. """ return self._file.get_size() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_mime_type') def get_mime_type(self): """Return the content mimetype. """ # possibly strip out charset encoding return self.get_content_type().split(';')[0].strip() security.declareProtected(SilvaPermissions.AccessContentsInformation, 'fulltext') def fulltext(self): """Return the content of this object without any markup """ converter = get_converter_for_mimetype(self.get_mime_type()) fulltext = [self.get_title()] if converter is None: return fulltext text = None filename = self.get_file_system_path() if filename is not None: text = converter.convert_file(filename) else: file_data = self.get_file() if file_data: text = converter.convert_string(file_data) if text: fulltext.append(text) return fulltext security.declareProtected(SilvaPermissions.View, 'get_download_url') def get_download_url(self, preview=False, request=None): if request is None: request = self.REQUEST url = getMultiAdapter((self, request), IContentURL).url(preview=preview) if preview: # In case of preview we add something that change at the # end of the url to prevent caching from the browser. url += '?' + str(int(time.time())) return url security.declareProtected(SilvaPermissions.View, 'tag') def tag(self, **kw): warnings.warn( 'tag have been replaced with get_html_tag. ' 'It will be removed, please update your code.', DeprecationWarning, stacklevel=2) return self.get_html_tag(**kw) security.declareProtected(SilvaPermissions.View, 'get_html_tag') def get_html_tag(self, preview=False, request=None, **extra_attributes): """ return xhtml tag Since 'class' is a Python reserved word, it cannot be passed in directly in keyword arguments which is a problem if you are trying to use 'tag()' to include a CSS class. The tag() method will accept a 'css_class' argument that will be converted to 'class' in the output tag to work around this. """ src = self.get_download_url(preview, request) title = self.get_title_or_id() if 'css_class' in extra_attributes: extra_attributes['class'] = extra_attributes['css_class'] del extra_attributes['css_class'] extra_html_attributes = [ '{name}="{value}"'.format(name=escape(name, 1), value=escape(value, 1)) for name, value in extra_attributes.items() ] return '<a href="%s" title="Download %s" %s>%s</a>' % ( src, self.get_filename(), extra_html_attributes, title) # checks where the mime type is text/* or javascript security.declareProtected(SilvaPermissions.AccessContentsInformation, 'is_text') def is_text(self): mimetype = self.get_mime_type() if ((mimetype.startswith('text/') and mimetype != 'text/rtf') or mimetype in ('application/x-javascript', 'application/xml', 'application/xhtml+xml')): return self.get_content_encoding() is None return False security.declareProtected(SilvaPermissions.AccessContentsInformation, 'is_text_editable') def is_text_editable(self): #size is editable if it is less than 150 KB return self.is_text() and (not self.get_file_size() > 153600) security.declareProtected(SilvaPermissions.View, 'get_text') def get_text(self): if not self.is_text(): raise TypeError("Content of Silva File is not text") return self.get_file() security.declareProtected(SilvaPermissions.View, 'get_file') def get_file(self): fd = self.get_file_fd() data = fd.read() fd.close() return data security.declareProtected(SilvaPermissions.View, 'get_file_fd') def get_file_fd(self): raise NotImplementedError security.declareProtected(SilvaPermissions.View, 'get_content_type') def get_content_type(self): return self._file.content_type security.declareProtected(SilvaPermissions.View, 'get_content_encoding') def get_content_encoding(self): return self._content_encoding # MODIFIERS security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_file') def set_file(self, stream, content_type=None, content_encoding=None): """Set data in _file object """ raise NotImplementedError security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_filename') def set_filename(self, filename): """Set filename """ self._filename = filename security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'get_file_system_path') def get_file_system_path(self): """Return path on filesystem for containing File. """ return None security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_content_type') def set_content_type(self, content_type): self._file.content_type = content_type security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_content_encoding') def set_content_encoding(self, content_encoding): self._content_encoding = content_encoding security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_text') def set_text(self, text): raise NotImplementedError
class SilvaSoftwareActivity(Content, SimpleItem, ExternalSource): """Collect activity from an RSS feed and generate statistics about it. """ grok.implements(ISilvaSoftwareActivity) meta_type = 'Silva Software Activity' security = ClassSecurityInfo() silvaconf.icon('SilvaSoftwareActivity.png') silvaconf.priority(9) _rss_url = None _data = None security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_rss_url') def get_rss_url(self): return self._rss_url security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_rss_url') def set_rss_url(self, url): self._rss_url = url security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_changes_since') def get_changes_since(self, days=31, empty=True): """Return the changes since the given number of days. """ if self._data is not None: today = datetime.date.today() today_day = today.toordinal() since = (today - datetime.timedelta(days)) since_day = since.toordinal() if not empty: return list(self._data.values(since_day)) data = self._data.items(since_day) result = [] for day, values in data: while day > since_day: result.append([]) since += datetime.timedelta(1) since_day = since.toordinal() result.append(values) since += datetime.timedelta(1) since_day = since.toordinal() if result: while today_day > since_day: result.append([]) since += datetime.timedelta(1) since_day = since.toordinal() return result return [] security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'refresh') def refresh(self): """Refresh the data stored. """ rss_url = self.get_rss_url() if rss_url is None: return if self._data is None: self._data = IOBTree() data = feedparser.parse(rss_url) changed = False for entry in data['entries']: date = datetime.date(*entry['updated_parsed'][:3]) key = date.toordinal() if key not in self._data: self._data[key] = Changes() change = Change(entry['id'], entry['author'], date, entry['summary']) changed = self._data[key].add(change) or changed if changed: self._p_changed = True def is_previewable(self): return False def to_html(self, content, request, **parameters): return silvaviews.render(self, request)
class SilvaSoftwareActivityAggregator(Content, SimpleItem, ExternalSource): """Aggregate multiple activities together. """ grok.implements(ISilvaSoftwareActivityAggregator) meta_type = 'Silva Software Activity Aggregator' security = ClassSecurityInfo() silvaconf.icon('SilvaSoftwareActivity.png') silvaconf.priority(9) _data = None _most_actives = [] security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_changes_since') def get_changes_since(self, days=31, empty=True): """Return the changes since the given number of days. """ if self._data is not None: today = datetime.date.today() today_day = today.toordinal() since = (today - datetime.timedelta(days)) since_day = since.toordinal() if not empty: return list(self._data.values(since_day)) data = self._data.items(since_day) result = [] for day, values in data: while day > since_day: result.append([]) since += datetime.timedelta(1) since_day = since.toordinal() result.append(values) since += datetime.timedelta(1) since_day = since.toordinal() if result: while today_day > since_day: result.append([]) since += datetime.timedelta(1) since_day = since.toordinal() return result return [] security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'refresh') def refresh(self): """Refresh the data stored. """ if self._data is None: self._data = IOBTree() counts = {} catalog = getUtility(ICatalogService) for brain in catalog(meta_type=['Silva Software Activity'], path='/'.join( self.get_container().getPhysicalPath())): activity = brain.getObject() counts[brain.content_intid] = 0 changes = activity.get_changes_since(empty=False) print activity.get_title(), len(changes) for change in changes: for commit in change: key = commit.date.toordinal() if key not in self._data: self._data[key] = Changes() self._data[key].add(commit) counts[brain.content_intid] += len(change) self._most_actives = map( operator.itemgetter(0), sorted(counts.items(), key=operator.itemgetter(1), reverse=True)) security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_most_active') def get_most_active(self, limit=5): get_activity = getUtility(IIntIds).getObject return map(lambda i: get_activity(i).get_container(), self._most_actives[:limit]) def is_previewable(self): return False def to_html(self, content, request, **parameters): return silvaviews.render(self, request)
class AutoTOC(Content, SimpleItem): __doc__ = _("""This is a special document type that automatically generates a Table of Contents. Usually it's used as the 'index' document of a folder. Then the parent folder displays a TOC when accessed (e.g. http://www.x.yz/silva/myFolder). The AutoTOC is configurable: it can display any selection of Silva content including assets, include descriptions or icons, be set to stop at a specific depth, and use various sorting methods. """) security = ClassSecurityInfo() meta_type = "Silva AutoTOC" grok.implements(IAutoTOC) silvaconf.icon('icons/autotoc.png') silvaconf.priority(0.2) _local_types = ['Silva Document', 'Silva Publication', 'Silva Folder'] _toc_depth = -1 _display_desc_flag = False # values: 'silva', 'alpha', 'reversealpha', 'chronmod', 'rchronmod' _sort_order = 'silva' _show_icon = False _show_container_link = False security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_toc_depth') def set_toc_depth(self, depth): self._toc_depth = depth security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_toc_depth') def get_toc_depth(self): """get the depth to which the toc will be rendered""" return self._toc_depth security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_show_container_link') def set_show_container_link(self, flag): self._show_container_link = flag security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_show_container_link') def get_show_container_link(self): """get the depth to which the toc will be rendered""" return self._show_container_link security.declareProtected(SilvaPermissions.View, 'get_local_types') def get_local_types(self): return self._local_types security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_local_types') def set_local_types(self, types): self._local_types = types security.declareProtected(SilvaPermissions.View, 'get_display_desc_flag') def get_display_desc_flag(self): return self._display_desc_flag security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_display_desc_flag') def set_display_desc_flag(self, flag): self._display_desc_flag = flag security.declareProtected(SilvaPermissions.View, 'get_show_icon') def get_show_icon(self): return self._show_icon security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_show_icon') def set_show_icon(self, flag): self._show_icon = flag security.declareProtected(SilvaPermissions.View, 'get_sort_order') def get_sort_order(self): return self._sort_order security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'set_sort_order') def set_sort_order(self, order): self._sort_order = order
class CodeSourceService(SilvaService): meta_type = 'Silva Code Source Service' grok.implements(ICodeSourceService, ISilvaConfigurableService) grok.name('service_codesources') silvaconf.icon('www/codesource_service.png') security = ClassSecurityInfo() manage_options = ({ 'label': 'Available Code Sources', 'action': 'manage_existing_codesources' }, { 'label': 'Install Code Sources', 'action': 'manage_install_codesources' }, { 'label': 'External Sources Errors', 'action': 'manage_sources_errors' }) + SilvaService.manage_options # This is used a marker in to be backward compatible. _installed_sources = None security.declareProtected('View management screens', 'find_installed_sources') def find_installed_sources(self): logger.info('search for code sources') self.clear_installed_sources() service = getUtility(IIntIds) for source in walk_silva_tree(self.get_root(), requires=ICodeSource): self._installed_sources.append(service.register(source)) security.declareProtected('Access contents information', 'get_installed_sources') def get_installed_sources(self): if self._installed_sources is not None: resolve = getUtility(IIntIds).getObject for source_id in self._installed_sources: try: yield resolve(source_id) except KeyError: pass security.declareProtected('View management screens', 'clear_installed_sources') def clear_installed_sources(self): self._installed_sources = [] security.declareProtected('View management screens', 'get_installable_sources') def get_installable_sources(self, refresh=False): if not refresh and hasattr(self.aq_base, '_v_installable_sources'): return self._v_installable_sources self._v_installable_sources = sources = [] for entry_point in iter_entry_points( 'Products.SilvaExternalSources.sources'): module = entry_point.load() directory = os.path.dirname(module.__file__) for source_identifier in os.listdir(directory): source_directory = os.path.join(directory, source_identifier) if not os.path.isdir(source_directory): continue source_files = os.listdir(source_directory) if CONFIGURATION_FILE not in source_files: continue source_location = ( entry_point.dist.project_name + ':' + source_directory[len(entry_point.dist.location):]) sources.append( CodeSourceInstallable( source_location, source_directory, extension=entry_point.dist.project_name)) return sources security.declareProtected('View management screens', 'get_installable_source') def get_installable_source(self, identifier=None, location=None): if identifier is not None: test = lambda s: s.identifier == identifier elif location is not None: test = lambda s: s.location == location else: raise NotImplementedError for source in self.get_installable_sources(): if test(source): yield source
class Root(Publication): """Root of Silva site. """ security = ClassSecurityInfo() meta_type = "Silva Root" # We do not want to register Root automaticaly. grok.implements(IRoot) silvaconf.icon('icons/root.png') silvaconf.zmi_addable(True) silvaconf.factory('manage_addRootForm') silvaconf.factory('manage_addRoot') _smi_skin = 'silva.ui.interfaces.ISilvaUITheme' _properties = Publication._properties + ({ 'id': '_smi_skin', 'label': 'Skin SMI', 'type': 'string', 'mode': 'w' }, ) def __init__(self, id): super(Root, self).__init__(id) # if we add a new root, version starts out as the software version self._content_version = self.get_silva_software_version() # MANIPULATORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'validate_wanted_quota') def validate_wanted_quota(self, value, REQUEST=None): """Validate the wanted quota is correct the current publication. """ if value < 0: # Quota can't be negative. return False if not value: # 0 or means no quota. return True # Quota can't be be bigger than the site quota. if self.service_extensions._site_quota: if self.service_extensions._site_quota < value: return False return True security.declareProtected(SilvaPermissions.ApproveSilvaContent, 'to_folder') def to_folder(self): """Don't do anything here. Can't do this with root. """ raise ContentError(_("Root cannot be converted to folder."), self) security.declareProtected(SilvaPermissions.ApproveSilvaContent, 'to_publication') def to_publication(self): """Don't do anything here. Can't do this with root. """ raise ContentError(_("Root cannot be converted to publication."), self) security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'add_silva_addable_forbidden') def add_silva_addable_forbidden(self, meta_type): """Add a meta_type that is forbidden from use in this site. """ addables_forbidden = getattr(self.aq_base, '_addables_forbidden', {}) addables_forbidden[meta_type] = 0 self._addables_forbidden = addables_forbidden security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'clear_silva_addables_forbidden') def clear_silva_addables_forbidden(self): """Clear out all forbidden addables; everything allowed now. """ self._addables_forbidden = {} # ACCESSORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_root') def get_root(self): """Get root of site. Can be used with acquisition get the 'nearest' Silva root. """ return self.aq_inner security.declareProtected(SilvaPermissions.ReadSilvaContent, 'is_silva_addable_forbidden') def is_silva_addable_forbidden(self, meta_type): """Return true if addable is forbidden to be used in this site. """ if not hasattr(self.aq_base, '_addables_forbidden'): return False return meta_type in self._addables_forbidden security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_current_quota') def get_current_quota(self): """Return the current quota value on the publication. """ site = getUtility(IExtensionService).get_site_quota() binding = getUtility(IMetadataService).getMetadata(self) try: local = int(binding.get('silva-quota', element_id='quota') or 0) if local < 0: local = 0 except KeyError: local = 0 if site and local: return min(site, local) if site: return site return local security.declarePublic('get_silva_software_version') def get_silva_software_version(self): """The version of the Silva software. """ return extensionRegistry.get_extension('Silva').version security.declareProtected(SilvaPermissions.ReadSilvaContent, 'get_silva_content_version') def get_silva_content_version(self): """Return the version of Silva content. This version is usually the same as the software version, but can be different in case the content was not yet updated. """ return getattr(self, '_content_version', 'before 0.9.2') security.declareProtected(SilvaPermissions.AccessContentsInformation, 'get_real_container') def get_real_container(self): """Get the container, even if we're a container. If we're the root object, returns None. Can be used with acquisition to get the 'nearest' container. """ return None
class SilvaFind(Query, Content, SimpleItem): __doc__ = _("""Silva Find is a powerful search feature that allows easy creation of search forms and result pages. Users can add a Find anywhere and define which fields to make searchable by site visitors and/or which fields to limit to a preset value. Users also can determine which fields should be displayed in the search results. All metadata sets/fields are supported.""") security = ClassSecurityInfo() meta_type = "Silva Find" grok.implements(IFind) silvaconf.icon('SilvaFind.png') def __init__(self, id): Content.__init__(self, id) Query.__init__(self) self.shownFields = PersistentMapping() self.shownResultsFields = PersistentMapping() # by default we only show fulltext search # and a couple of resultfields self.shownFields['fulltext'] = True self.shownResultsFields['link'] = True self.shownResultsFields['ranking'] = True self.shownResultsFields['resultcount'] = True self.shownResultsFields['icon'] = True self.shownResultsFields['date'] = True self.shownResultsFields['textsnippet'] = True self.shownResultsFields['thumbnail'] = True self.shownResultsFields['breadcrumbs'] = True # ACCESSORS security.declareProtected(SilvaPermissions.View, 'getPublicResultFields') def getPublicResultFields(self): return filter(lambda field: self.isResultShown(field.getName()), self.getResultFields()) security.declareProtected(SilvaPermissions.View, 'getPublicSearchFields') def getPublicSearchFields(self): return filter(lambda field: self.isCriterionShown(field.getName()), self.getSearchFields()) security.declareProtected(SilvaPermissions.View, 'isCriterionShown') def isCriterionShown(self, fieldName): return self.shownFields.get(fieldName, False) security.declareProtected(SilvaPermissions.View, 'isResultShown') def isResultShown(self, fieldName): return self.shownResultsFields.get(fieldName, False) security.declareProtected(SilvaPermissions.View, 'havePublicSearchFields') def havePublicSearchFields(self): # BBB map(bool) is here for previously non-boolean stored values return reduce(operator.or_, map(bool, self.shownFields.values())) security.declareProtected(SilvaPermissions.View, 'searchResults') def searchResults(self, request={}, validate=True): options = self.getSearchCriterias(request) if validate: queryEmpty = True for key, value in options.items(): if key in ['path', 'meta_type']: # these fields do not count as a real search query # they are always there to filter unwanted results continue if type(value) is unicode and value.strip(): queryEmpty = False break elif type(value) is list: queryEmpty = False break query = options.get('fulltext', '').strip() if query and query[0] in ['?', '*']: raise ValueError( _(u'Search query can not start with wildcard character.')) if queryEmpty: raise ValueError( _(u'You need to fill at least one field in the search form.' )) options['publication_status'] = ['public'] catalog = self.get_root().service_catalog try: results = catalog.searchResults(options) except ParseError: raise ValueError( _(u'Search query contains only common or reserved words.')) return results def getSearchCriterias(self, request): options = {} for field in self.getSearchFields(): name = field.getName() if (self.shownFields.get(name, False) or name == 'path'): queryPart = getMultiAdapter((field, self, request), IQueryPart) value = queryPart.getIndexValue() if value is None: value = '' options[queryPart.getIndexId()] = value return options
class SimpleMember(Member, Security, ZMIObject): """Silva Simple Member""" grok.implements(interfaces.IEditableMember) security = ClassSecurityInfo() meta_type = 'Silva Simple Member' silvaconf.icon('icons/member.png') silvaconf.factory('manage_addSimpleMemberForm') silvaconf.factory('manage_addSimpleMember') # BBB _avatar = None def __init__(self, id): self.id = id self._title = id self._fullname = None self._email = None self._avatar = None self._creation_datetime = self._modification_datetime = DateTime() self._is_approved = 0 security.declarePrivate('allowed_roles') def allowed_roles(self): return roleinfo.ASSIGNABLE_ROLES security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'set_fullname') def set_fullname(self, fullname): """set the full name""" self._fullname = fullname security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'set_email') def set_email(self, email): """ set the email address. (does not test, if email address is valid) """ self._email = email security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'set_avatar') def set_avatar(self, avatar): """Set the email address to be used by gravatar""" self._avatar = avatar security.declareProtected(SilvaPermissions.ChangeSilvaAccess, 'approve') def approve(self): """Approve the member""" self._is_approved = 1 # ACCESSORS security.declareProtected(SilvaPermissions.AccessContentsInformation, 'userid') def userid(self): """userid """ return self.id security.declareProtected(SilvaPermissions.AccessContentsInformation, 'fullname') def fullname(self): """fullname """ if self._fullname is None: return self.id return self._fullname security.declareProtected(SilvaPermissions.AccessContentsInformation, 'email') def email(self): """email """ return self._email security.declareProtected(SilvaPermissions.AccessContentsInformation, 'extra') def extra(self, name): """Return bit of extra information, keyed by name. """ #For CachedMember accessing of the avatar tag #Should be 'avatar_tag:SIZE' -- ie, 'avatar_tag:32' if name.startswith("avatar_tag"): return self.avatar_tag(name.split(':')[1]) return None security.declareProtected(SilvaPermissions.AccessContentsInformation, 'avatar') def avatar(self): """Return the email address to be used by gravatar. Return '' if no address has been specified. """ return self._avatar if self._avatar is not None else self._email security.declareProtected(SilvaPermissions.AccessContentsInformation, 'is_approved') def is_approved(self): """is_approved """ return self._is_approved
class SourceAsset(VersionedNonPublishable): """A source asset stores a external source and a set of its parameters.x """ meta_type = "Silva Source Asset" grok.implements(ISourceAsset) silvaconf.icon('www/code_source_asset.png') silvaconf.version_class(SourceAssetVersion) security = ClassSecurityInfo() security.declareProtected(permissions.AccessContentsInformation, 'get_parameters_form') def get_parameters_form(self): return None security.declarePrivate('get_source') def get_viewable_source(self): viewable = self.get_viewable() if viewable is not None: try: return viewable.get_source() except SourceError: pass return None security.declareProtected(permissions.AccessContentsInformation, 'to_html') def to_html(self, content, request, **parameters): viewable = self.get_viewable() if viewable is not None: return viewable.get_controller(request).render() # Should we put an error message instead ? return '' def get_description(self): source = self.get_viewable_source() if source is not None: return source.get_description() return _('Broken or missing source.') security.declareProtected(permissions.AccessContentsInformation, 'get_icon') def get_icon(self): source = self.get_viewable_source() if source is not None: return source.get_icon() return None security.declareProtected(permissions.AccessContentsInformation, 'is_usable') def is_usable(self): source = self.get_viewable_source() if source is not None: return True return False security.declareProtected(permissions.AccessContentsInformation, 'is_previewable') def is_previewable(self, **parameters): source = self.get_viewable_source() if source is not None: return source.is_previewable() return False security.declareProtected(permissions.AccessContentsInformation, 'is_cacheable') def is_cacheable(self, **parameters): source = self.get_viewable_source() if source is not None: return source.is_cacheable() return False