Пример #1
0
class MockupAsset(Asset, SimpleItem):
    """A mockup non-publishable.
    """
    grok.implements(IMockupAsset)
    meta_type = 'Mockup Asset'
    silvaconf.priority(-10)
    silvaconf.icon('tests/mockers.png')
Пример #2
0
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()
Пример #3
0
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')()
Пример #4
0
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())
Пример #5
0
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
Пример #6
0
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)
Пример #7
0
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'))
Пример #8
0
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')
Пример #9
0
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',
        ]
Пример #10
0
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()
Пример #11
0
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',
        ]
Пример #13
0
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
Пример #14
0
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)
Пример #15
0
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 &#8216;comma separated values&#8217;. 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})
Пример #17
0
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)
Пример #18
0
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)
Пример #19
0
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
Пример #20
0
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)
Пример #21
0
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')
Пример #22
0
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)
Пример #25
0
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
Пример #26
0
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
Пример #27
0
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
Пример #28
0
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
Пример #29
0
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