1: import logging
1: from BeautifulSoup import BeautifulSoup
1: from zope.formlib import form
1: from zope.interface import implements
1: from zope import schema
1: from zope.testbrowser.browser import Browser
1: from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
1: from Products.CMFCore.utils import getToolByName
1: from Products.CMFPlone.utils import safe_unicode
1: from plone.memoize.instance import memoize
1: from plone.portlets.interfaces import IPortletDataProvider
1: from plone.app.portlets.portlets import base
1: from vnccollab.theme import messageFactory as _
1: from vnccollab.common.portlets import deferred
1: from vnccollab.theme.portlets.zimbra_mail import logException
1: logger = logging.getLogger('vnccollab.theme.EtherpadListsPortlet')
2: class IEtherpadsListPortlet(IPortletDataProvider):
1: header = schema.TextLine(
1: title=_(u"Header"),
1: description=_(u"Header of the portlet."),
1: required=True,
1: default=u'Your Pads')
1: url = schema.URI(
1: title=_(u"Etherpad Root URL"),
1: description=_(u"Root url to your Etherpad service. If not set here, "
"then user settings will be checked for etherpad root url "
" service."),
1: required=False,
1: default='https://vitaliy.etherpad.zdemo.vnc.biz')
1: count = schema.Int(
1: title=_(u"Number of pads to display"),
1: description=u'',
1: required=True,
1: default=10)
1: username = schema.ASCIILine(
1: title=_(u"Username"),
1: description=_(u"If not set, etherpad_username property of "
"authenticated user will be used."),
1: required=False,
1: default='')
1: password = schema.Password(
1: title=_(u"Password"),
1: description=_(u"If not set, etherpad_password property of authenticated"
" user will be used."),
1: required=False,
1: default=u'')
2: class Assignment(base.Assignment):
1: implements(IEtherpadsListPortlet)
1: header = u'Your Pads'
1: url = 'https://vitaliy.etherpad.zdemo.vnc.biz'
1: count = 10
1: username = ''
1: password = u''
1: @property
def title(self):
"""Return portlet header"""
>>>>>> return self.header
1: def __init__(self, header=u'Your Pads',
1: url='https://vitaliy.etherpad.zdemo.vnc.biz',
1: count=10, username='', password=u''):
>>>>>> self.header = header
>>>>>> self.url = url
>>>>>> self.count = count
>>>>>> self.username = username
>>>>>> self.password = password
2: class Renderer(deferred.DeferredRenderer):
1: render = ZopeTwoPageTemplateFile('templates/etherpads_list.pt')
1: def refresh(self):
>>>>>> self.pads = self.getPads()
1: @memoize
def getPads(self):
>>>>>> pads = []
>>>>>> username, password, url = self.getUserData()
>>>>>> if not (username and password and url):
>>>>>> return ()
# try to request etherpad for page with table of pads
>>>>>> try:
>>>>>> content = self._getPadsPage()
>>>>>> except:
>>>>>> logException(_(u"Error during fetching pads from %s" % url),
>>>>>> context=self.context, logger=logger)
>>>>>> return ()
# try to parse html page into pads
>>>>>> try:
>>>>>> pads = self._parsePadsPage(content, self.trail_url(url),
>>>>>> self.data.count)
>>>>>> except:
>>>>>> logException(_(u"Error during parsing pads page from %s" % url),
>>>>>> context=self.context, logger=logger)
>>>>>> return ()
>>>>>> return tuple(pads)
1: def _parsePadsPage(self, content, base_url, limit):
>>>>>> pads = []
>>>>>> soup = BeautifulSoup(content)
# go over pads table rows, skipping first header row
>>>>>> counter = 0
>>>>>> for row in soup.find('table', id='padtable').findAll('tr')[1:]:
>>>>>> if limit and limit <= counter:
>>>>>> break
# get row cells
>>>>>> ctitle, cdate, ceditors = row.findAll('td')[:3]
>>>>>> if not (ctitle and cdate and ceditors):
>>>>>> continue
# prepare pad url
>>>>>> link = ctitle.find('a')
>>>>>> if not link:
>>>>>> continue
>>>>>> url = '%s%s' % (base_url, link.get('href'))
>>>>>> if not url:
>>>>>> continue
# prepare editors
>>>>>> editors = []
>>>>>> for editor in ceditors.findAll('a'):
>>>>>> editors.append({
>>>>>> 'name': editor.text,
>>>>>> 'url': '%s%s' % (base_url, editor.get('href'))
})
>>>>>> pads.append({
>>>>>> 'url': url,
>>>>>> 'title': link.text,
>>>>>> 'date': cdate.text,
>>>>>> 'editors': tuple(editors)
})
>>>>>> counter += 1
>>>>>> return pads
1: def _getPadsPage(self):
>>>>>> username, password, url = self.getUserData()
# login
>>>>>> browser = Browser()
>>>>>> browser.open('%s/ep/account/sign-in' % self.trail_url(url))
>>>>>> browser.getControl(name='email').value = username
>>>>>> browser.getControl(name='password').value = password
>>>>>> browser.getForm(id='signin-form').submit()
# open pads table page
>>>>>> browser.getLink('Pads').click()
>>>>>> return safe_unicode(browser.contents)
1: @memoize
def root_url(self):
"""Return url w/o trailing slash prepared either from portlet or
user settings.
Used in template.
"""
>>>>>> return self.trail_url(self.getUserData()[2])
1: def trail_url(self, url=None):
"""Remove trailing slash from url."""
>>>>>> if url and url.endswith('/'):
>>>>>> return url[:-1]
>>>>>> return url
1: @memoize
def getUserData(self):
"""Returns username, password and root etherpad url for user.
Returns tuple of:
(username, password, root etherpad url)
"""
>>>>>> mtool = getToolByName(self.context, 'portal_membership')
>>>>>> member = mtool.getAuthenticatedMember()
>>>>>> username, password, url = self.data.username, self.data.password, \
>>>>>> self.data.url
# take username and password from authenticated user Etherpad creds
>>>>>> if not (username and password):
>>>>>> username, password = member.getProperty('etherpad_username', ''), \
>>>>>> member.getProperty('etherpad_password', '')
# if not set globally, take url from user settings
>>>>>> if not url:
>>>>>> url = member.getProperty('etherpad_url', '')
# password could contain non-ascii chars, ensure it's properly encoded
>>>>>> return username, safe_unicode(password).encode('utf-8'), url
1: @property
def title(self):
"""return title of feed for portlet"""
>>>>>> return self.data.header
2: class AddForm(base.AddForm):
1: form_fields = form.Fields(IEtherpadsListPortlet)
1: label = _(u"Add Etherpad Lists portlet")
1: description = _(u"A portlet displaying list of Etherpad pads.")
1: def create(self, data):
>>>>>> return Assignment(**data)
2: class EditForm(base.EditForm):
1: form_fields = form.Fields(IEtherpadsListPortlet)
1: label = _(u"Edit Etherpad Lists portlet")
1: description = _(u"A portlet displaying list of Etherpad pads.")