Test coverage for vnccollab.theme.browser.viewlets

vnccollab/      covered 69% (1245 of 4098 uncovered)
    theme/      covered 69% (1245 of 4098 uncovered)
        browser/      covered 74% (507 of 1985 uncovered)
            viewlets.py      covered 88% (57 of 497 uncovered)

    1: from urllib import quote_plus
    1: import logging
    1: import os.path
    1: from pyactiveresource.activeresource import ActiveResource
       
    1: from Acquisition import aq_inner
    1: from DateTime import DateTime
       
    1: from zope.app.component.hooks import getSite
    1: from zope.interface import alsoProvides, providedBy, Interface
    1: from zope.component import getMultiAdapter, queryMultiAdapter, getUtility
    1: from zope.i18nmessageid import MessageFactory
    1: from zope.viewlet.interfaces import IViewlet
       
    1: from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
    1: from Products.CMFCore.utils import getToolByName
    1: from Products.CMFCore.interfaces import IActionCategory, IAction
    1: from Products.CMFCore.ActionInformation import ActionInfo
    1: from Products.CMFPlone.utils import safe_unicode, normalizeString, parent
    1: from Products.CMFPlone.i18nl10n import monthname_msgid, weekdayname_msgid
    1: from Products.CMFPlone.interfaces.siteroot import IPloneSiteRoot
       
    1: from plone.i18n.normalizer.interfaces import IIDNormalizer
    1: from plone.app.contentmenu.menu import FactoriesSubMenuItem
    1: from plone.app.viewletmanager.manager import BaseOrderedViewletManager
    1: from plone.app.layout.viewlets import common
    1: from plone.app.layout.viewlets.interfaces import IPortalHeader
    1: from plone.memoize.instance import memoize
    1: from plone.registry.interfaces import IRegistry
    1: from plone.portlets.interfaces import IPortletManager, IPortletRenderer
       
    1: from Products.Carousel.browser.viewlet import CarouselViewlet
       # from jarn.xmpp.core.browser.viewlet import XMPPViewlet
       
    1: from collective.quickupload.portlet.quickuploadportlet import JAVASCRIPT
       
    1: from vnccollab.theme.portlets.zimbra_mail import logException
    1: from vnccollab.theme import messageFactory as _
    1: from vnccollab.theme.avatar import IAvatarUtil
    1: from vnccollab.theme.config import FOOTER_LINKS_CAT
    1: from vnccollab.theme.browser.interfaces import IVNCCollabHtmlHead
    1: from vnccollab.theme.portlets import world_clock
    1: from vnccollab.theme.settings import IWorldClockSettings
    1: from vnccollab.theme.util import getZimbraLiveAnnotatedTasks
       
    1: from plone.app.layout.links.viewlets import FaviconViewlet
       
       
    1: _pl = MessageFactory('plonelocales')
    1: logger = logging.getLogger('vnccollab.theme.RelatedRedmineTicketsViewlet')
       
       
    2: class TopRatedViewlet(common.ViewletBase):
           """Renders list of most rated items under given container.
       
           Rating system by cioppino.twothumbs.
    1:     """
       
    1:     def update(self):
    1:         catalog = getToolByName(self.context, 'portal_catalog')
    1:         elems = []
    1:         for brain in catalog(path={'depth': 20,
    1:             'query': '/'.join(self.context.getPhysicalPath())},
    1:             sort_on='avg_ratings',
    4:             sort_order='reverse'):
       
                   # skip item if nobody voted yet
    3:             if brain.positive_ratings == 0 and brain.total_down_ratings == 0:
>>>>>> continue
3: elems.append({ 3: 'title': _(safe_unicode(brain.Title)), 3: 'desc': _(safe_unicode(brain.Description)), 3: 'url': brain.getURL(), 3: 'type': normalizeString(brain.portal_type, encoding='utf-8'), 3: 'rating': {'total': brain.avg_ratings, 3: 'liked': brain.positive_ratings, 3: 'disliked': brain.total_down_ratings}}) 1: self.elems = tuple(elems) 2: class ActionsListViewlet(common.ViewletBase): """Renders internal ActionsItem List object view. Gets first found ActionsItem List object in first level hierarchy. 1: """ 1: def update(self): 1: self.todo = None 66: for obj in self.context.objectValues(): 65: if getattr(obj, 'portal_type', '') == 'ActionItemList':
>>>>>> self.todo = obj
>>>>>> break
2: class LoginViewlet(common.ViewletBase): 1: """Most methods are copied over from login portlet renderer""" 1: template = ViewPageTemplateFile('templates/login.pt') 1: anon_template = ViewPageTemplateFile('templates/anon_login.pt') 1: def __init__(self, *args, **kw): 71: super(LoginViewlet, self).__init__(*args, **kw) 71: self.membership = getToolByName(self.context, 'portal_membership') 71: self.context_state = getMultiAdapter((self.context, self.request), 71: name=u'plone_context_state') 71: self.portal_state = getMultiAdapter((self.context, self.request), 71: name=u'plone_portal_state') 71: self.pas_info = getMultiAdapter((self.context, self.request), 71: name=u'pas_info') 1: def render(self): 69: mt = getToolByName(self.context, 'portal_membership') 69: if mt.isAnonymousUser(): 33: return self.anon_template() else: 36: return self.template() 1: def show(self): 36: if not self.portal_state.anonymous(): 36: return False
>>>>>> if not self.pas_info.hasLoginPasswordExtractor():
>>>>>> return False
>>>>>> return True
# page = self.request.get('URL', '').split('/')[-1] # return page not in ('login_form', '@@register') 1: @property def available(self):
>>>>>> return self.auth() is not None and self.show()
1: def register_url(self): 64: registry = getUtility(IRegistry) 64: show_register = registry.get(self._k('show_register_url'), True) 64: register_url = registry.get(self._k('register_url'), '') 64: if not show_register: 2: return '' 62: if not register_url: 58: register_url = '%s/register' % self.portal_state.portal_url() 62: return register_url 1: def help_url(self): 33: registry = getUtility(IRegistry) 33: return registry.get(self._k('help_url'), '') 1: def login_url(self): 64: registry = getUtility(IRegistry) 64: show_login = registry.get(self._k('show_login_url'), True) 64: login_url = registry.get(self._k('login_url'), '') 64: if not show_login: 2: return '' 62: if not login_url: 58: login_url = '%s/login_form' % self.portal_state.portal_url() 62: return login_url 1: def _k(self, k): 289: return 'vnccollab.theme.settings' \ 289: + '.IAnonymousHomepageSettings.{0}'.format(k) 1: def mail_password_form(self):
>>>>>> return '%s/mail_password_form' % self.portal_state.portal_url()
1: def login_name(self): 36: auth = self.auth() 36: name = None 36: if auth is not None: 36: name = getattr(auth, 'name_cookie', None) 36: if not name: 36: name = '__ac_name' 36: return name 1: def login_password(self): 36: auth = self.auth() 36: passwd = None 36: if auth is not None: 36: passwd = getattr(auth, 'pw_cookie', None) 36: if not passwd: 36: passwd = '__ac_password' 36: return passwd 1: def join_action(self): 2: context = self.context 2: tool = getToolByName(context, 'portal_actions') 2: join = tool.listActionInfos(action_chain='user/join', object=context) 2: if len(join) > 0: 1: return join[0]['url'] 1: return None 1: def can_register(self): 2: if getToolByName(self.context, 'portal_registration', None) is None: 1: return False 1: return self.membership.checkPermission('Add portal member', 1: self.context) 1: def can_request_password(self): 2: return self.membership.checkPermission('Mail forgotten password', 2: self.context) 1: @memoize 1: def auth(self, _marker=[]): 36: acl_users = getToolByName(self.context, 'acl_users') 36: return getattr(acl_users, 'credentials_cookie_auth', None) 2: class HeaderTimeViewlet(common.ViewletBase): 1: """Returns current date and time in local format""" 1: def update(self): 1: super(HeaderTimeViewlet, self).update() 1: date = DateTime() 1: self.day = date.day() 1: self.month = _pl(monthname_msgid(int(date.strftime('%m'))), 1: default=safe_unicode(date.Month())) 1: self.dayname = _pl(weekdayname_msgid(int(date.strftime('%w'))), 1: default=safe_unicode(date.DayOfWeek())) 1: self.datetime = self.toLocalizedTime(date, long_format=True) 1: def toLocalizedTime(self, time, long_format=None, time_only=None): """Convert time to localized time """ 2: util = getToolByName(self.context, 'translation_service') 2: return util.ulocalized_time(time, long_format, time_only, self.context, 2: domain='plonelocales') 2: class PathBarViewlet(common.PathBarViewlet): 1: template = ViewPageTemplateFile('templates/path_bar.pt') 1: def render(self): 70: mt = getToolByName(self.context, 'portal_membership') 70: if mt.isAnonymousUser(): 33: return u'' else: 37: return self.template() 2: class FooterViewlet(common.FooterViewlet): 1: index = ViewPageTemplateFile('templates/footer.pt') 1: def update(self): 69: super(FooterViewlet, self).update() 69: self.columns = columns = {} 69: context = aq_inner(self.context) 69: actions_tool = getToolByName(context, 'portal_actions') # check if we got root category for all column links 69: if not FOOTER_LINKS_CAT in actions_tool.objectIds():
>>>>>> return
# prepare expression context for evaluating TAL expressions 69: ec = actions_tool._getExprContext(context) # go over root category and collect all sub-categories 69: container = actions_tool[FOOTER_LINKS_CAT] 69: cat_ids = container.objectIds() 276: for cid in ('column1', 'column2', 'column3'): # skip not existing categories 207: if cid not in cat_ids:
>>>>>> continue
207: cat = container[cid] 207: if not IActionCategory.providedBy(cat):
>>>>>> continue
# prepare category actions 207: actions = [] 621: for action in cat.objectValues(): # look only for actions 414: if not IAction.providedBy(action):
>>>>>> continue
# create actioninfo object to compile and render TAL expressions # and check if action is available in current circumstances 414: info = ActionInfo(action, ec) 414: if not (info['visible'] and info['allowed'] and 381: info['available']): 66: continue # and finally extract all required details from action 348: desc = action.getProperty('description', None) or None 348: if desc is not None:
>>>>>> desc = _(safe_unicode(desc))
348: actions.append({ 348: 'id': info['id'], 348: 'title': _(safe_unicode(info['title'])), 348: 'desc': desc, 348: 'url': info['url'] }) # finally add category to be rendered as footer column 207: columns[cid] = { 207: 'title': _(safe_unicode(cat.getProperty('title', ''))), 207: 'actions': tuple(actions) } 69: self.columns = columns 2: class PersonalBarViewlet(common.PersonalBarViewlet): 1: index = ViewPageTemplateFile('templates/personal_bar.pt') 1: def update(self): 69: super(PersonalBarViewlet, self).update() # get personal user image 69: self.user_image = None 69: if not self.anonymous: 36: mtool = getToolByName(self.context, 'portal_membership') # if no userid passes it'll return portrait of logged in user 36: portrait = mtool.getPersonalPortrait() 36: if portrait is not None: 36: self.user_image = portrait.absolute_url() # render languages viewlet 69: context = aq_inner(self.context) 69: languages = u'' 69: manager = BaseOrderedViewletManager() 69: alsoProvides(manager, IPortalHeader) 69: viewlet = queryMultiAdapter((context, self.request, self.view, 69: manager), IViewlet, name='vnccollab.theme.languageselector') 69: if viewlet is not None: 69: viewlet = viewlet.__of__(context) 69: viewlet.update() 69: languages = viewlet.render() 69: self.languages = languages # Get css style for image avatar 69: self.avatar_width = 80 69: self.avatar_height = 80 69: self.avatar_style = '' 69: if self.user_image is not None: 36: img_name = os.path.basename(self.user_image) 36: if img_name != 'defaultUser.png':
>>>>>> img = context.portal_memberdata.portraits[img_name]
>>>>>> avatar = getUtility(IAvatarUtil)
>>>>>> self.avatar_width, self.avatar_height, self.avatar_style = avatar.style(img, (80, 80))
2: class VNCCarouselViewlet(CarouselViewlet): 1: """Customize template to fix javascript code""" 1: index = ViewPageTemplateFile('templates/carousel_viewlet.pt') 2: class AnonHomepageCarouselViewlet(CarouselViewlet): 1: template = ViewPageTemplateFile('templates/carousel_viewlet.pt') 1: def render(self): 3: mt = getToolByName(self.context, 'portal_membership') 3: if mt.isAnonymousUser(): 2: return self.template() else: 1: return u'' 2: class VNCCollabHeaderViewlet(common.ViewletBase): 1: """Viewlet that inserts vnc header manager into plone header manager""" 1: def available(self): """Available only if carousel is set on current folder"""
>>>>>> context = aq_inner(self.context)
>>>>>> manager = BaseOrderedViewletManager()
>>>>>> alsoProvides(manager, IVNCCollabHtmlHead)
>>>>>> viewlet = queryMultiAdapter((context, self.request, self.view, manager),
>>>>>> IViewlet, name='vnccollab.theme.headercarousel')
>>>>>> if viewlet is None:
>>>>>> return False
>>>>>> viewlet = viewlet.__of__(context)
>>>>>> viewlet.update()
>>>>>> return viewlet.available
2: class RelatedRedmineTicketsViewlet(common.ViewletBase): 1: """Lists redmine tickets assigned to current object""" 1: def update(self): 11: self.tickets = () 11: if IPloneSiteRoot in providedBy(self.context):
>>>>>> return
11: tickets = [] # check if settings are configured # check user redmine credentials and redmine url/field id 11: registry = getUtility(IRegistry) 11: url = registry.get('vnccollab.theme.redmine.url') 11: field_id = registry.get('vnccollab.theme.redmine.plone_uid_field_id') 11: username, password = self.getAuthCredentials() 11: if username and password and url and field_id:
>>>>>> Issue = type("Issue", (ActiveResource,), {'_site': url, '_user':
>>>>>> username, '_password': password})
# do actual calls to redmine
>>>>>> try:
# fetch opened issues belonging to authenticated user
>>>>>> uuid = self.context.UID()
>>>>>> data = Issue.find(**{'cf_%d' % field_id: uuid,
>>>>>> 'status_id': 'o', 'sort': 'updated_on:desc'})
>>>>>> except Exception:
>>>>>> logException(_(u"Error during fetching redmine tickets %s" %
>>>>>> url), context=self.context, logger=logger)
>>>>>> return
>>>>>> for item in data:
>>>>>> info = item.to_dict()
# skip invalid entries
>>>>>> if not info.get('id') or not info.get('subject'):
>>>>>> continue
>>>>>> tickets.append({
>>>>>> 'id': info['id'],
>>>>>> 'title': safe_unicode(info['subject']),
>>>>>> 'body': safe_unicode(info.get('description', '')),
>>>>>> 'url': '%s/issues/%s' % (url, info['id'])
}) 11: self.tickets = tuple(tickets) 1: @memoize def getAuthCredentials(self): """Returns username and password for redmine user.""" # take username and password from authenticated user Zimbra creds 11: mtool = getToolByName(self.context, 'portal_membership') 11: member = mtool.getAuthenticatedMember() 11: username, password = member.getProperty('redmine_username', ''), \ 11: member.getProperty('redmine_password', '') # password could contain non-ascii chars, ensure it's properly encoded 11: return username, safe_unicode(password).encode('utf-8') 2: class RelatedZimbraTasksViewlet(common.ViewletBase): 1: """Lists zimbra tasks assigned to current object""" 1: def update(self): 11: self.tasks = getZimbraLiveAnnotatedTasks(self.context) 2: class WorldClockViewlet(common.ViewletBase): """Shows world clock. It basically re-uses World Clock portlet code. 1: """ 1: def update(self): 1: context = aq_inner(self.context) 1: portal = getToolByName(context, 'portal_url').getPortalObject() 1: manager = getUtility(IPortletManager, name='plone.rightcolumn', 1: context=portal) # get settings from registry 1: registry = getUtility(IRegistry) 1: try: 1: settings = registry.forInterface(IWorldClockSettings)
>>>>>> except KeyError:
# in case settings are not there yet
>>>>>> self.world_clock = ''
>>>>>> return
1: tz_1 = settings.tz_1 1: skin_1 = settings.skin_1 1: radius_1 = settings.radius_1 1: no_seconds_1 = settings.no_seconds_1 1: tz_2 = settings.tz_2 1: skin_2 = settings.skin_2 1: radius_2 = settings.radius_2 1: no_seconds_2 = settings.no_seconds_2 1: tz_3 = settings.tz_3 1: skin_3 = settings.skin_3 1: radius_3 = settings.radius_3 1: no_seconds_3 = settings.no_seconds_3 1: assignment = world_clock.Assignment(header=u'', tz_1=tz_1, 1: skin_1=skin_1, radius_1=radius_1, no_seconds_1=no_seconds_1, 1: tz_2=tz_2, skin_2=skin_2, radius_2=radius_2, 1: no_seconds_2=no_seconds_2, tz_3=tz_3, skin_3=skin_3, 1: radius_3=radius_3, no_seconds_3=no_seconds_3) 1: renderer = queryMultiAdapter((context, self.request, self.view, manager, 1: assignment), IPortletRenderer) 1: renderer.update() 1: self.world_clock = renderer.render() 2: class IExternalEditable(Interface): 1: """Marker Interface for objects than can be edited by zopeedit.""" 2: class ZopeEditViewlet(common.ViewletBase): 1: """Link for external editor""" 1: def external_editor_url(self): 1: path = self.context.absolute_url_path() 1: p = os.path.dirname(path) 1: me = os.path.basename(path) + '.zem' 1: return os.path.join(p, 'externalEdit_', me) 2: class AddContentAreaViewlet(common.ViewletBase): 1: """Add new content form""" 1: index = ViewPageTemplateFile('templates/add_content_area.pt') 1: def getAddLinks(self): """Returns list with info of the allowed types to create using the add wizard. """ 37: result = self._get_default_allowed_types() 137: ids = [x['id'] for x in result] # Adds allowed types in current context 37: context = aq_inner(self.context) 37: extend = self._allowed_types(context) 407: for item in extend: 370: if item['id'] not in ids: 270: result.append(item) 37: return result 1: def _get_default_allowed_types(self): '''Default allowed types: the ones you can add to your member folder.''' 37: member_folder = self._get_member_home() 37: if member_folder is None: 27: return [] else: 10: return self._allowed_types(member_folder) 1: def _get_member_home(self): '''Returns the cuerrent user's folder.''' 37: mtool = getToolByName(self.context, 'portal_membership') 37: member = mtool.getAuthenticatedMember() 37: if member is None:
>>>>>> return None
37: portal = getToolByName(self.context, 'portal_url').getPortalObject() 37: try: 37: id = member.id.replace('@', '-40') 37: home = portal['Members'][id] 27: except: 27: home = None 37: return home 1: def _all_user_selectable_types(self, site): """ List user-selectable content types. We cannot use the method provided by the IPortalState utility view, because the vocabulary factory must be available in contexts where there is no HTTP request (e.g. when installing add-on product). This code is copied from https://github.com/plone/plone.app.layout/tree/master/plone/app/layout/globals/portal.py """ 47: context = aq_inner(site) 47: site_properties = getToolByName(context, "portal_properties").site_properties 47: not_searched = site_properties.getProperty('types_not_searched', []) 47: portal_types = getToolByName(context, "portal_types") 47: types = portal_types.listContentTypes() # Get list of content type ids which are not filtered out 1457: prepared_types = [t for t in types if t not in not_searched] 47: ignored = ['Cast Update', 'Cast Comment', 'Topic'] 658: return [portal_types[id] for id in prepared_types if id not in ignored] 1: def _allowed_types(self, context): '''Return info of the types that can be added to the context.''' 47: submenu = FactoriesSubMenuItem(context, self.request) 47: folder = self.getFolder(context) 47: folder_url = folder.absolute_url() 47: idnormalizer = getUtility(IIDNormalizer) 47: result = [] #for atype in submenu._addableTypesInContext(folder): 517: for atype in self._all_user_selectable_types(folder): 470: id = atype.getId() 470: result.append({ 470: 'id': id, 470: 'title': atype.Title(), 470: 'desc': atype.Description(), 470: 'url': '%s/createObject?type_name=%s' % (folder_url, 470: quote_plus(id)), 470: 'icon': '%s/add_content_area/metabox_icon_%s.png' % ( 470: self.site_url, idnormalizer.normalize(id)) }) 47: return result 1: def getUploadUrl(self): """ return upload url in current folder """ 37: context = aq_inner(self.context) 37: ploneview = context.restrictedTraverse('@@plone') 37: folder_url = ploneview.getCurrentFolderUrl() 37: return '%s/@@wizard_uploader' % folder_url 1: def upload_javascript(self): 37: return JAVASCRIPT.replace('.QuickUploadPortlet', '#createWizard') 1: @memoize def getFolder(self, context): 47: context = aq_inner(context) 47: submenu = FactoriesSubMenuItem(context, self.request) 47: if submenu.context_state.is_default_page(): 4: return parent(context) 43: return submenu._addContext() 2: class AddButtonViewlet(common.ViewletBase): 1: '''Overrides SearchBoxViewlet for folders in Stream Mode.''' 1: template = ViewPageTemplateFile('templates/addbutton.pt') 1: def render(self): 69: mt = getToolByName(self.context, 'portal_membership') 69: if mt.isAnonymousUser(): 33: return u'' else: 36: return self.template() 1: try: 1: import vnccollab.cloudcast 1: from vnccollab.cloudcast.interfaces import ICastContainer, \ ICastsContainer, ICast
>>>>>> except ImportError:
>>>>>> CAST_ENABLED = False
else: 1: CAST_ENABLED = True 2: class CastViewletBase(object): 1: def get_cast_url(self): 39: if not CAST_ENABLED:
>>>>>> return False
39: catalog = getToolByName(self.context, 'portal_catalog') 39: portal_path = getToolByName(self.context, 'portal_url').getPortalPath() 39: casts = catalog(portal_type='CastsContainer', path={'query': 39: portal_path, 'depth': 1}, sort_on='getObjPositionInParent') 39: if len(casts) > 0: 2: return casts[0].getURL() # no casts container in site root, search for any other casts container 37: casts = catalog(portal_type='CastsContainer', 37: sort_on='getObjPositionInParent') 37: if len(casts) > 0:
>>>>>> return casts[0].getURL()
37: return False 1: def check_in_cast(self): 41: if not CAST_ENABLED:
>>>>>> return False
41: cast_interfaces = [ICastsContainer, ICastContainer, ICast] 158: for cast in cast_interfaces: 119: if cast.providedBy(self.context): 2: return True 39: return False # class CustomXMPPViewlet(XMPPViewlet, CastViewletBase): # # index = ViewPageTemplateFile('templates/xmpp_viewlet.pt') # # def update(self): # super(CustomXMPPViewlet, self).update() # # # prepare link to first cast container on the site, of course if cast # # feature is enabled # self.cast_url = self.get_cast_url() # self.cast_url = self.cast_url if self.cast_url else '' 2: class HeaderLinksIconsViewlet(FaviconViewlet): 1: render = ViewPageTemplateFile('templates/favicon.pt') 2: class TabsViewlet(common.ViewletBase, CastViewletBase): 1: index = ViewPageTemplateFile('templates/tabs.pt') 1: @property def available(self): 72: mt = getToolByName(self.context, 'portal_membership') 72: if mt.isAnonymousUser(): 33: return False else: 39: return True 1: def update(self): 72: self.portal_tabs = [] 72: if not self.available: 33: return self.portal_tabs = [ 39: {'name': 'Content', 39: 'description': 'content', 39: 'id': 'content', 39: 'url': getSite().absolute_url(), 39: 'selected': not self.check_in_cast() }] 39: cast_url = self.get_cast_url() 39: if cast_url != False: 2: self.portal_tabs.append({ 2: 'name': 'Cast', 2: 'description': 'cast', 2: 'id': 'cast', 2: 'url': cast_url, 2: 'selected': self.check_in_cast()}) 2: class SearchBoxViewlet(common.ViewletBase): 1: """Overrides SearchBoxViewlet for folders in Stream Mode.""" 1: template = ViewPageTemplateFile('templates/searchbox.pt') 1: def render(self): 69: mt = getToolByName(self.context, 'portal_membership') 69: if mt.isAnonymousUser(): 33: return u'' else: 36: return self.template() 2: class EmptyViewlet(common.ViewletBase): 1: """Empty viewlet to remove previous registrations""" 1: def update(self):
>>>>>> pass
1: def render(self):
>>>>>> return u''