class CoverSettingsEditForm(controlpanel.RegistryEditForm): schema = ICoverSettings label = _(u'Cover Settings') description = _(u'Settings for the scmos.cover package') # def updateFields(self): # super(CoverSettingsEditForm, self).updateFields() # self.fields['layouts'].widgetFactory = TextLinesFieldWidget def updateWidgets(self): super(CoverSettingsEditForm, self).updateWidgets() self.widgets['available_tiles'].style = u'min-width: 200px;' self.widgets['searchable_content_types'].style = u'min-width: 200px;' self.widgets['styles'].rows = 6 self.widgets['styles'].style = u'max-width: 250px;'
class IContentBodyTile(IPersistentCoverTile): uuid = schema.TextLine( title=_(u'UUID'), required=False, readonly=True, )
class PFGTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/pfg.pt') is_editable = True is_configurable = True short_name = _(u'msg_short_name_pfg', default=u'FormGen') def body(self): body = '' uuid = self.data.get('uuid', None) try: obj = uuid and uuidToObject(uuid) if obj is not None: body = obj.restrictedTraverse('fg_embedded_view_p3')() except Unauthorized: body = '' return body def populate_with_object(self, obj): super(PFGTile, self).populate_with_object(obj) data = { 'title': safe_unicode(obj.Title()), 'description': safe_unicode(obj.Description()), 'uuid': IUUID(obj), } data_mgr = ITileDataManager(self) data_mgr.set(data) def accepted_ct(self): """Return 'FormFolder' as the only content type accepted in the tile. """ return ['FormFolder']
def handleSave(self, action): data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return # Traverse to a new tile in the context, with no data typeName = self.tileType.__name__ tile = self.context.restrictedTraverse('@@{0}/{1}'.format( typeName, self.tileId)) tile_conf_adapter = getMultiAdapter((self.context, self.request, tile), ITilesConfigurationScreen) tile_conf_adapter.set_configuration(data) # notify about modification notify(ObjectModifiedEvent(self.context)) api.portal.show_message(_(u'Tile configuration saved.'), self.request, type='info') # Look up the URL - We need to redirect to the layout view, since # there's the only way from where a user would access the configuration contextURL = absoluteURL(tile.context, self.request) layoutURL = '{0}/layoutedit'.format(contextURL) self.request.response.redirect(layoutURL)
def handleCancel(self, action): api.portal.show_message(_(u'Tile configuration cancelled.'), self.request, type='info') contextURL = absoluteURL(self.context, self.request) layoutURL = '{0}/layoutedit'.format(contextURL) self.request.response.redirect(layoutURL)
class IEmbedTile(IPersistentCoverTile): write_permission(embed='scmos.cover.EmbedCode') embed = schema.Text( title=_(u'Embedding code'), required=False, ) title = schema.TextLine( title=_(u'Title'), required=False, ) description = schema.Text( title=_(u'Description'), required=False, )
class IRefresh(model.Schema): """Reload the current page after a certain amount of time.""" model.fieldset('settings', fields=['enable_refresh', 'ttl']) enable_refresh = schema.Bool( title=_(u'Enable refresh'), description=_(u'Enable refresh of the current page.'), default=False, ) ttl = schema.Int( title=_(u'Time to live'), description=_( u'Number of seconds after which to reload the current page.'), default=300, )
class IPFGTile(IPersistentCoverTile): title = schema.TextLine( title=_(u'Title'), required=False, ) description = schema.Text( title=_(u'Description'), required=False, ) uuid = schema.TextLine( title=_(u'UUID'), required=False, readonly=True, )
class IBannerTile(IPersistentCoverTile): title = schema.TextLine( title=_(u'Title'), required=False, ) form.omitted(IDefaultConfigureForm, 'remote_url') remote_url = schema.URI( title=_(u'label_remote_url', default=u'URL'), description=_(u'help_remote_url', default=u'Use absolute links only.'), required=False, ) image = field.NamedBlobImage( title=_(u'Image'), required=False, ) form.omitted(IDefaultConfigureForm, 'alt_text') alt_text = schema.TextLine( title=_(u'label_alt_text', default=u'Alternative Text'), description=_( u'help_alt_text', default= u'Provides a textual alternative to non-text content in web pages.' ), # noqa E501 required=False, ) uuid = schema.TextLine( # FIXME: this must be schema.ASCIILine() title=_(u'UUID'), required=False, readonly=True, )
class IRichTextTile(IPersistentCoverTile): text = RichText(title=u'Text') uuid = schema.TextLine( title=_(u'UUID'), required=False, readonly=True, )
def update(self): super(CustomEditForm, self).update() tile = self.getTile() if (not IDeferSecurityCheck.providedBy(self.request) and not tile.isAllowedToEdit()): # if IDeferSecurityCheck is provided by the request, # we're not going to worry about security, perms not set up yet raise Unauthorized( _(u'You are not allowed to add this kind of tile'))
class ContentBodyTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/contentbody.pt') is_editable = False is_configurable = False short_name = _(u'msg_short_name_contentbody', default=u'Content Body') @property def is_empty(self): return not self.data.get('uuid', False) def body(self): """Return the body text of the related object.""" uuid = self.data.get('uuid', None) try: obj = uuid and uuidToObject(uuid) except Unauthorized: return # TODO: handle exception and show message on template if obj is None: return '' # obj was deleted try: return obj.getText() # Archetypes except AttributeError: return obj.text.output if obj.text is not None else '' # Dexterity def populate_with_object(self, obj): super(ContentBodyTile, self).populate_with_object(obj) data = { 'uuid': IUUID(obj), } data_mgr = ITileDataManager(self) data_mgr.set(data) def accepted_ct(self): """Return 'Document' and 'News Item' as the only content types accepted in the tile. """ return ['Document', 'News Item'] def item_url(self): uuid = self.data.get('uuid', None) try: obj = uuidToObject(uuid) except Unauthorized: obj = None if obj: return obj.absolute_url()
class IFileTile(IPersistentCoverTile): title = schema.TextLine( title=_(u'Title'), required=False, ) description = schema.Text( title=_(u'Description'), required=False, ) download = schema.TextLine( title=_(u'Download link'), required=False, readonly=True, # this field can not be edited or configured ) uuid = schema.TextLine( title=_(u'UUID'), required=False, readonly=True, )
def populate_with_object(self, obj): super(CollectionTile, self).populate_with_object(obj) # check permission if obj.portal_type in self.accepted_ct(): header = safe_unicode( obj.Title()) # use collection's title as header footer = _(u'More…') # XXX: can we use field's default? data_mgr = ITileDataManager(self) data_mgr.set({ 'header': header, 'footer': footer, 'uuid': IUUID(obj), })
def replace_with_uuids(self, uuids): """ Replaces the whole list of items with a new list of items :param uuids: The list of objects' UUIDs to be used :type uuids: List of strings """ if not self.isAllowedToEdit(): raise Unauthorized( _('You are not allowed to add content to this tile')) data_mgr = ITileDataManager(self) old_data = data_mgr.get() # Clean old data old_data['uuids'] = dict() data_mgr.set(old_data) # Repopulate with clean list self.populate_with_uuids(uuids)
class Bootstrap2(BaseGrid): """Bootstrap 2 grid system (12 columns).""" ncolumns = 12 title = _(u'Bootstrap 2') def columns_formatter(self, columns): prefix = 'span' for column in columns: width = column.get('column-size', 1) column['class'] = self.column_class + ' ' + (prefix + str(width)) if 'css-class' in column: column['class'] += ' {0}'.format( column['css-class']) return columns
def populate_with_uuids(self, uuids): """ Add a list of elements to the list of items. This method will append new elements to the already existing list of items :param uuids: The list of objects' UUIDs to be used :type uuids: List of strings """ if not self.isAllowedToEdit(): raise Unauthorized( _('You are not allowed to add content to this tile')) self.set_limit() data_mgr = ITileDataManager(self) old_data = data_mgr.get() if old_data['uuids'] is None: # If there is no content yet, just assign an empty dict old_data['uuids'] = dict() uuids_dict = old_data.get('uuids') if not isinstance(uuids_dict, dict): # Make sure this is a dict uuids_dict = old_data['uuids'] = dict() if uuids_dict and len(uuids_dict) > self.limit: # Do not allow adding more objects than the defined limit return order_list = [int(val.get('order', 0)) for key, val in uuids_dict.items()] if len(order_list) == 0: # First entry order = 0 else: # Get last order position and increment 1 order_list.sort() order = order_list.pop() + 1 for uuid in uuids: if uuid not in uuids_dict: entry = dict() entry[u'order'] = six.text_type(order) uuids_dict[uuid] = entry order += 1 old_data['uuids'] = uuids_dict data_mgr.set(old_data)
class EmbedTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/embed.pt') is_configurable = True is_editable = True is_droppable = False short_name = _(u'msg_short_name_embed', default=u'Embed') def is_empty(self): return not (self.data.get('embed', None) or self.data.get('title', None) or self.data.get('description', None)) def accepted_ct(self): """Return an empty list as no content types are accepted.""" return []
class RichTextTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/richtext.pt') is_configurable = True short_name = _(u'msg_short_name_richtext', default=u'Rich Text') def getText(self): """ Return the rich text stored in the tile. """ text = '' if self.data['text']: text = self.data['text'] # We expect that the text has a mimeType and an output # attribute, but someone may be using a different widget # returning a simple unicode, so check that. if not isinstance(text, six.string_types): transformer = ITransformer(self.context, None) if transformer is not None: text = transformer(text, 'text/x-html-safe') return text def populate_with_object(self, obj): super(RichTextTile, self).populate_with_object(obj) if safe_hasattr(obj, 'getRawText'): text = obj.getRawText().decode('utf-8') else: # Probably a dexterity item. This is already unicode. text = obj.text.raw value = RichTextValue(raw=text, mimeType='text/x-html-safe', outputMimeType='text/x-html-safe') data = { 'text': value, 'uuid': IUUID(obj), } data_mgr = ITileDataManager(self) data_mgr.set(data) def accepted_ct(self): """Return 'Document' as the only content type accepted in the tile.""" return ['Document']
class Deco16Grid(BaseGrid): """Deco grid system (16 columns).""" title = _(u'Deco (16 columns)') ncolumns = 16 column_class = 'cell' def columns_formatter(self, columns): w = 'width-' p = 'position-' offset = 0 for column in columns: width = column.get('column-size', 1) column['class'] = self.column_class + ' ' + (w + str(width)) + ' ' + (p + str(offset)) if 'css-class' in column: column['class'] += ' {0}'.format( column['css-class']) offset = offset + width return columns
def handleSave(self, action): data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return tile = self.getTile() # We need to check first for existing content in order not not loose # fields that weren't sent with the form dataManager = ITileDataManager(tile) old_data = dataManager.get() for item in data: old_data[item] = data[item] dataManager.set(old_data) api.portal.show_message(_(u'Tile saved'), self.request, type='info') # Look up the URL - we need to do this after we've set the data to # correctly account for transient tiles tileURL = absoluteURL(tile, self.request) self.request.response.redirect(tileURL)
def validate(self, value): """Validate the value on time to live input""" if value <= 0: raise Invalid(_(u'Value must be greater than zero.'))
class IBasicTile(IPersistentCoverTile): title = schema.TextLine( title=_(u'Title'), required=False, ) description = schema.Text( title=_(u'Description'), required=False, ) form.omitted(IDefaultConfigureForm, 'remote_url') remote_url = schema.URI( title=_(u'label_remote_url', default=u'URL'), description=_( u'help_remote_url', default=u'Leave this field empty to use the URL of the referenced content. ' u'Enter a URL to override it (use absolute links only).'), required=False, ) image = NamedImage( title=_(u'Image'), required=False, ) form.omitted(IDefaultConfigureForm, 'alt_text') alt_text = schema.TextLine( title=_( u'label_alt_text', default=u'Alternative Text'), description=_( u'help_alt_text', default=u'Provides a textual alternative to non-text content in web pages.'), # noqa E501 required=False, ) form.omitted('date') form.no_omit(IDefaultConfigureForm, 'date') date = schema.Datetime( title=_(u'Date'), required=False, readonly=False, ) form.omitted('subjects') form.no_omit(IDefaultConfigureForm, 'subjects') form.widget(subjects='z3c.form.browser.textarea.TextAreaFieldWidget') subjects = schema.Tuple( title=_(u'label_categories', default=u'Categories'), required=False, value_type=schema.TextLine(), missing_value=(), ) uuid = schema.TextLine( title=_(u'UUID'), required=False, readonly=True, )
class ListTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/list.pt') is_configurable = True is_droppable = True is_editable = True short_name = _(u'msg_short_name_list', default=u'List') limit = 5 def results(self): """Return the list of objects stored in the tile as UUID. If an UUID has no object associated with it, removes the UUID from the list. :returns: a list of objects. """ self.set_limit() # always get the latest data uuids = ITileDataManager(self).get().get('uuids', None) results = list() if uuids: ordered_uuids = [(k, v) for k, v in uuids.items()] ordered_uuids.sort(key=lambda x: int(x[1]['order'])) for uuid in [i[0] for i in ordered_uuids]: obj = uuidToObject(uuid) if obj: results.append(obj) else: # maybe the user has no permission to access the object # so we try to get it bypassing the restrictions catalog = api.portal.get_tool('portal_catalog') brain = catalog.unrestrictedSearchResults(UID=uuid) if not brain: # the object was deleted; remove it from the tile self.remove_item(uuid) logger.debug( 'Non-existent object {0} removed from tile'.format(uuid)) # noqa: E501 return results[:self.limit] def is_empty(self): return self.results() == [] def Date(self, obj): # XXX: different from Collection tile, List tile returns objects # instead of brains in its `results` function. I think we # were looking for some performace gains there but in this # case we need to go back to brains to avoid crazy stuff on # trying to guess the name of the method returning the # start date of an Event or an Event-like content type. We # probably want to rewrite the `results` funtion but we have # to be careful because there must be tiles derived from it, # like the Carousel tile catalog = api.portal.get_tool('portal_catalog') brain = catalog(UID=self.get_uuid(obj)) assert len(brain) == 1 # nosec return super(ListTile, self).Date(brain[0]) # TODO: get rid of this by replacing it with the 'count' field def set_limit(self): for field in self.get_configured_fields(): if field and field.get('id') == 'uuids': self.limit = int(field.get('size', self.limit)) def populate_with_object(self, obj): """ Add an object to the list of items :param obj: [required] The object to be added :type obj: Content object """ super(ListTile, self).populate_with_object(obj) # check permission self.populate_with_uuids([self.get_uuid(obj)]) def populate_with_uuids(self, uuids): """ Add a list of elements to the list of items. This method will append new elements to the already existing list of items :param uuids: The list of objects' UUIDs to be used :type uuids: List of strings """ if not self.isAllowedToEdit(): raise Unauthorized( _('You are not allowed to add content to this tile')) self.set_limit() data_mgr = ITileDataManager(self) old_data = data_mgr.get() if old_data['uuids'] is None: # If there is no content yet, just assign an empty dict old_data['uuids'] = dict() uuids_dict = old_data.get('uuids') if not isinstance(uuids_dict, dict): # Make sure this is a dict uuids_dict = old_data['uuids'] = dict() if uuids_dict and len(uuids_dict) > self.limit: # Do not allow adding more objects than the defined limit return order_list = [int(val.get('order', 0)) for key, val in uuids_dict.items()] if len(order_list) == 0: # First entry order = 0 else: # Get last order position and increment 1 order_list.sort() order = order_list.pop() + 1 for uuid in uuids: if uuid not in uuids_dict: entry = dict() entry[u'order'] = six.text_type(order) uuids_dict[uuid] = entry order += 1 old_data['uuids'] = uuids_dict data_mgr.set(old_data) def replace_with_uuids(self, uuids): """ Replaces the whole list of items with a new list of items :param uuids: The list of objects' UUIDs to be used :type uuids: List of strings """ if not self.isAllowedToEdit(): raise Unauthorized( _('You are not allowed to add content to this tile')) data_mgr = ITileDataManager(self) old_data = data_mgr.get() # Clean old data old_data['uuids'] = dict() data_mgr.set(old_data) # Repopulate with clean list self.populate_with_uuids(uuids) def remove_item(self, uuid): """ Removes an item from the list :param uuid: [required] uuid for the object that wants to be removed :type uuid: string """ super(ListTile, self).remove_item(uuid) # check permission data_mgr = ITileDataManager(self) old_data = data_mgr.get() uuids = data_mgr.get()['uuids'] if uuid in uuids.keys(): del uuids[uuid] old_data['uuids'] = uuids data_mgr.set(old_data) def get_uuid(self, obj): """Return the UUID of the object. :param obj: [required] :type obj: content object :returns: the object's UUID """ return IUUID(obj, None) def get_alt(self, obj): """Return the alt attribute for the image in the obj.""" return obj.Description() or obj.Title() # XXX: refactoring the tile's schema should be a way to avoid this def get_configured_fields(self): # Override this method, since we are not storing anything # in the fields, we just use them for configuration tileType = queryUtility(ITileType, name=self.__name__) conf = self.get_tile_configuration() fields = getFieldsInOrder(tileType.schema) results = [] for name, obj in fields: field = {'id': name, 'title': obj.title} if name in conf: field_conf = conf[name] if ('visibility' in field_conf and field_conf['visibility'] == u'off'): # If the field was configured to be invisible, then just # ignore it continue if 'htmltag' in field_conf: # If this field has the capability to change its html tag # render, save it here field['htmltag'] = field_conf['htmltag'] if 'format' in field_conf: field['format'] = field_conf['format'] if 'imgsize' in field_conf: field['scale'] = field_conf['imgsize'] if 'size' in field_conf: field['size'] = field_conf['size'] results.append(field) return results def thumbnail(self, item): """Return the thumbnail of an image if the item has an image field and the field is visible in the tile. :param item: [required] :type item: content object """ if self._has_image_field(item) and self._field_is_visible('image'): tile_conf = self.get_tile_configuration() image_conf = tile_conf.get('image', None) if image_conf: scaleconf = image_conf['imgsize'] # scale string is something like: 'mini 200:200' and # we need the name only: 'mini' if scaleconf == '_original': scale = None else: scale = scaleconf.split(' ')[0] scales = item.restrictedTraverse('@@images') return scales.scale('image', scale) def _get_image_position(self): """Return the image position as configured on the tile. :returns: 'left' or 'right' """ tile_conf = self.get_tile_configuration() image_conf = tile_conf.get('image', None) if image_conf: return image_conf.get('position', u'left') @property def tile_title(self): return self.data['tile_title'] @property def more_link(self): if not (self.data['more_link'] and self.data['more_link_text']): return None pc = api.portal.get_tool('portal_catalog') brainz = pc(UID=self.data['more_link']) if not len(brainz): return None return { 'href': brainz[0].getURL(), 'text': self.data['more_link_text'], } @view.memoize def get_image_position(self): return self._get_image_position() def _get_title_tag(self, item): """Return the HTML code used for the title as configured on the tile. :param item: [required] :type item: content object """ tag = '<{heading}><a href="{href}">{title}</a></{heading}>' if self._field_is_visible('title'): tile_conf = self.get_tile_configuration() title_conf = tile_conf.get('title', None) if title_conf: heading = title_conf.get('htmltag', 'h2') href = item.absolute_url() title = item.Title() return tag.format(heading=heading, href=href, title=title) @view.memoize def get_title_tag(self, item): return self._get_title_tag(item)
class ICoverSettings(model.Schema): """ Interface for the control panel form. """ layouts = schema.Dict( title=_(u'Layouts'), required=True, key_type=schema.TextLine(title=_(u'Name')), value_type=schema.TextLine(title=_(u'Layout')), readonly=True, # FIXME: we have no widget for this field yet ) available_tiles = schema.List( title=_(u'Available tiles'), description=_(u'This tiles will be available for layout creation.'), required=True, default=DEFAULT_AVAILABLE_TILES, value_type=schema.Choice(vocabulary='scmos.cover.EnabledTiles'), ) searchable_content_types = schema.List( title=_(u'Searchable Content Types'), description=_(u'Only objects of these content types will be searched ' u'on the content chooser.'), required=False, default=DEFAULT_SEARCHABLE_CONTENT_TYPES, # we are going to list only the main content types in the widget value_type=schema.Choice( vocabulary='scmos.cover.AvailableContentTypes'), ) form.widget(styles='z3c.form.browser.textlines.TextLinesFieldWidget') styles = schema.Set( title=_(u'Styles'), description=_( u'Enter a list of styles to appear in the style pulldown. ' u'Format is title|className, one per line.'), required=False, default=set(), value_type=schema.ASCIILine(title=_(u'CSS Classes')), ) grid_system = schema.Choice( title=_(u'Grid System'), description=_(u'Choose a grid system'), required=True, default=DEFAULT_GRID_SYSTEM, vocabulary='scmos.cover.GridSystems', )
class IListTile(IPersistentCoverTile): uuids = schema.Dict( title=_(u'Elements'), key_type=schema.TextLine(), value_type=schema.Dict( key_type=schema.TextLine(), value_type=schema.TextLine(), ), required=False, ) form.omitted('uuids') # XXX: this field should be used to replace the 'limit' attribute form.omitted('count') form.no_omit(IDefaultConfigureForm, 'count') count = schema.Int( title=_(u'Number of items to display'), required=False, default=5, ) form.omitted('title') form.no_omit(IDefaultConfigureForm, 'title') title = schema.TextLine( title=_(u'Title'), required=False, ) form.omitted('description') form.no_omit(IDefaultConfigureForm, 'description') description = schema.Text( title=_(u'Description'), required=False, ) form.omitted('image') form.no_omit(IDefaultConfigureForm, 'image') image = NamedBlobImage( title=_(u'Image'), required=False, ) form.omitted('date') form.no_omit(IDefaultConfigureForm, 'date') date = schema.Datetime( title=_(u'Date'), required=False, ) tile_title = schema.TextLine(title=_(u'Tile Title'), required=False) form.omitted('tile_title') form.no_omit(ITileEditForm, 'tile_title') more_link = schema.TextLine(title=_('Show more... link'), required=False) form.omitted('more_link') form.no_omit(ITileEditForm, 'more_link') form.widget(more_link='scmos.cover.tiles.edit_widgets.more_link.MoreLinkFieldWidget') more_link_text = schema.TextLine(title=_('Show more... link text'), required=False) form.omitted('more_link_text') form.no_omit(ITileEditForm, 'more_link_text')
class BasicTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/basic.pt') is_configurable = True short_name = _(u'msg_short_name_basic', default=u'Basic') @memoizedproperty def brain(self): uuid = self.data.get('uuid') results = api.content.find(UID=uuid) assert len(results) <= 1 # nosec return results[0] if results else None def Date(self): # self.brain is None when the tile was populated by editing it if self.brain: return super(BasicTile, self).Date(self.brain) def is_empty(self): """Check if there is content to be shown respecting permissions.""" if self.brain: return False # we have two different use cases here: # * the user has no permission to access the content (e.g. Private state) # * the tile was manually populated without dropping content on it catalog = api.portal.get_tool('portal_catalog') uuid = self.data.get('uuid') results = catalog.unrestrictedSearchResults(UID=uuid) assert len(results) <= 1 if results: return True # user has no permission to access the content # check if tile was manually populated return not [i for i in self.data.values() if i] def getURL(self): """Return the URL of the referenced object or the value stored in remote_url field. """ remote_url = self.data.get('remote_url') if remote_url: return remote_url if self.brain: return self.brain.getURL() def Subject(self): """ Return the categories of the original object (AKA keywords, tags or labels). """ if self.brain: return self.brain.Subject def populate_with_object(self, obj): super(BasicTile, self).populate_with_object(obj) title = safe_unicode(obj.Title()) description = safe_unicode(obj.Description()) image = self.get_image_data(obj) if image: # clear scales if new image is getting saved self.clear_scales() # initialize the tile with all fields needed for its rendering # note that we include here 'date' and 'subjects', but we do not # really care about their value: they came directly from the catalog # brain data = { 'title': title, 'description': description, 'uuid': IUUID(obj), 'date': True, 'subjects': True, 'image': image, # FIXME: https://github.com/scmos.scmos.cover/issues/778 'alt_text': description or title, } data_mgr = ITileDataManager(self) data_mgr.set(data) msg = 'tile "{0}"" populated with data: {1}' logger.debug(msg.format(self.id, data)) @property def alt(self): """Return alternative text dealing with form init issues.""" alt_text = self.data['alt_text'] return alt_text if alt_text is not None else u''
class FileTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/file.pt') is_configurable = False # TODO: make the tile configurable is_editable = True is_droppable = True short_name = _(u'msg_short_name_file', default=u'File') def get_content_type(self, obj): """Return MIME type for both, Archetypes and Dexterity items.""" try: return obj.getContentType() # Archetypes except AttributeError: return obj.file.contentType # Dexterity def getBestIcon(self, obj): """Find most specific icon for a Dexterity object. This is a simplified version of the `getBestIcon` script included in Products.Archetypes `archetypes` skin. Should be probably included in plone.app.contenttypes. """ mtr = api.portal.get_tool('mimetypes_registry') content_type = obj.file.contentType try: lookup = mtr.lookup(content_type) except MimeTypeException: return None if lookup: mti = lookup[0] return mti.icon_path return None def download_widget(self): """ Returns a download link for the file associated with the tile. """ obj = uuidToObject(self.data['uuid']) if obj: url = obj.absolute_url() portal_url = obj.portal_url() content_type = self.get_content_type(obj) try: # Archetypes icon = obj.getBestIcon() mime = obj.lookupMime(content_type) size = obj.get_size() except AttributeError: # Dexterity icon = self.getBestIcon(obj) mime = lookupMime(obj, content_type) size = obj.file.size return get_download_html(url, portal_url, icon, mime, size) def is_empty(self): return not (self.data.get('title', None) or self.data.get( 'description', None) or self.data.get('uuid', None)) def populate_with_object(self, obj): super(FileTile, self).populate_with_object(obj) # check permissions if obj.portal_type not in self.accepted_ct(): return data = { 'title': safe_unicode(obj.Title()), 'description': safe_unicode(obj.Description()), 'download': True, 'uuid': IUUID(obj), } data_mgr = ITileDataManager(self) data_mgr.set(data) def accepted_ct(self): """Return 'File' as the only content type accepted in the tile.""" return ['File']
class BannerTile(PersistentCoverTile): index = ViewPageTemplateFile('templates/banner.pt') is_configurable = True is_editable = True is_droppable = True short_name = _(u'msg_short_name_banner', default=u'Banner') def populate_with_object(self, obj): """Tile can be populated with any content type with image or getImage attribute; in this case we're not going to take care of any modification of the original object; we just copy the data to the tile and deal with it. """ if obj.portal_type not in self.accepted_ct(): return super(BannerTile, self).populate_with_object(obj) # check permissions if obj.portal_type == 'Link': try: remote_url = obj.getRemoteUrl() # Archetypes except AttributeError: remote_url = obj.remoteUrl # Dexterity else: remote_url = obj.absolute_url() if obj.portal_type in get_types_use_view_action_in_listings(): remote_url += '/view' image = self.get_image_data(obj) if image: # clear scales if new image is getting saved self.clear_scales() obj = aq_base(obj) # avoid acquisition title = safe_unicode(obj.Title()) description = safe_unicode(obj.Description()) data_mgr = ITileDataManager(self) data_mgr.set({ 'title': title, 'description': description, 'uuid': IUUID(obj), 'image': image, # FIXME: https://github.com/scmos.scmos.cover/issues/778 'alt_text': description or title, 'remote_url': remote_url, }) def getRemoteUrl(self): """Return the remote URL field.""" return self.data.get('remote_url') or u'' # deal with None values @property def is_empty(self): return not (self.data.get('title') or self.has_image or self.getRemoteUrl()) @property def css_class(self): tile_conf = self.get_tile_configuration() image_conf = tile_conf.get('image', None) if image_conf: css_class = image_conf['position'] return css_class @property def htmltag(self): tile_conf = self.get_tile_configuration() title_conf = tile_conf.get('title', None) if title_conf: htmltag = title_conf['htmltag'] return htmltag @property def alt(self): """Return alternative text dealing with form init issues.""" alt_text = self.data['alt_text'] return alt_text if alt_text is not None else u''
class CalendarTile(PersistentCoverTile): """Calendar Tile code is coppied from plone.app.portlet Portlet Calendar """ index = ViewPageTemplateFile('templates/calendar.pt') is_configurable = False is_editable = False is_droppable = False short_name = _(u'msg_short_name_calendar', default=u'Calendar') def __init__(self, context, request): super(CalendarTile, self).__init__(context, request) self._setup() def _setup(self): context = aq_inner(self.context) self.calendar = getToolByName(context, 'portal_calendar') self._ts = getToolByName(context, 'translation_service') self.url_quote_plus = quote_plus self.now = localtime() self.yearmonth = yearmonth = self.getYearAndMonthToDisplay() self.year = year = yearmonth[0] self.month = month = yearmonth[1] self.showPrevMonth = yearmonth > (self.now[0] - 1, self.now[1]) self.showNextMonth = yearmonth < (self.now[0] + 1, self.now[1]) self.prevMonthYear, self.prevMonthMonth = self.getPreviousMonth(year, month) self.nextMonthYear, self.nextMonthMonth = self.getNextMonth(year, month) self.monthName = PLMF(self._ts.month_msgid(month), default=self._ts.month_english(month)) def accepted_ct(self): """Return an empty list as no content types are accepted.""" return [] def getEventsForCalendar(self): context = aq_inner(self.context) year = self.year month = self.month portal_state = getMultiAdapter((self.context, self.request), name='plone_portal_state') navigation_root_path = portal_state.navigation_root_path() weeks = self.calendar.getEventsForCalendar(month, year, path=navigation_root_path) for week in weeks: for day in week: daynumber = day['day'] if daynumber == 0: continue day['is_today'] = self.isToday(daynumber) if day['event']: cur_date = DateTime(year, month, daynumber) localized_date = [self._ts.ulocalized_time(cur_date, context=context, request=self.request)] day['eventstring'] = '\n'.join(localized_date + [ ' {0}'.format(self.getEventString(e)) for e in day['eventslist']]) day['date_string'] = '{0}-{1}-{2}'.format(year, month, daynumber) return weeks def getEventString(self, event): start = event['start'] and ':'.join(event['start'].split(':')[:2]) or '' end = event['end'] and ':'.join(event['end'].split(':')[:2]) or '' title = safe_unicode(event['title']) or u'event' if start and end: eventstring = '{0}-{1} {2}'.format(start, end, title) elif start: # can assume not event['end'] eventstring = '{0} - {1}'.format(start, title) elif event['end']: # can assume not event['start'] eventstring = '{0} - {1}'.format(title, end) else: # can assume not event['start'] and not event['end'] eventstring = title return eventstring def getYearAndMonthToDisplay(self): session = None request = self.request # First priority goes to the data in the REQUEST year = request.get('year', None) month = request.get('month', None) # Next get the data from the SESSION if self.calendar.getUseSession(): session = request.get('SESSION', None) if session: if not year: year = session.get('calendar_year', None) if not month: month = session.get('calendar_month', None) # Last resort to today if not year: year = self.now[0] if not month: month = self.now[1] # try to transform to number but fall back to current # date if this is ambiguous try: year, month = int(year), int(month) except (TypeError, ValueError): year, month = self.now[:2] # Store the results in the session for next time if session: session.set('calendar_year', year) session.set('calendar_month', month) # Finally return the results return year, month def getPreviousMonth(self, year, month): if month == 0 or month == 1: month, year = 12, year - 1 else: month -= 1 return (year, month) def getNextMonth(self, year, month): if month == 12: month, year = 1, year + 1 else: month += 1 return (year, month) def getWeekdays(self): """Returns a list of Messages for the weekday names.""" weekdays = [] # list of ordered weekdays as numbers for day in self.calendar.getDayNumbers(): weekdays.append(PLMF(self._ts.day_msgid(day, format='s'), default=self._ts.weekday_english(day, format='a'))) return weekdays def isToday(self, day): """Returns True if the given day and the current month and year equals today, otherwise False. """ return ( self.now[2] == day and self.now[1] == self.month and self.now[0] == self.year) def getReviewStateString(self): states = self.calendar.getCalendarStates() return ''.join(map(lambda x: 'review_state={0}&'.format(self.url_quote_plus(x)), states)) def getEventTypes(self): types = self.calendar.getCalendarTypes() return ''.join(map(lambda x: 'Type={0}&'.format(self.url_quote_plus(x)), types)) def getQueryString(self): request = self.request query_string = request.get('orig_query', request.get('QUERY_STRING', None)) if len(query_string) == 0: query_string = '' else: query_string = '{0}&'.format(query_string) return query_string