merge branch master

This commit is contained in:
Helmut Merz 2015-10-10 11:48:52 +02:00
commit 1800fe7c9e
11 changed files with 87 additions and 28 deletions

View file

@ -737,7 +737,9 @@ on data provided in this form:
>>> component.provideAdapter(NameChooser) >>> component.provideAdapter(NameChooser)
>>> request = TestRequest(form={'title': u'Test Note', >>> request = TestRequest(form={'title': u'Test Note',
... 'form.type': u'.loops/concepts/note'}) ... 'form.type': u'.loops/concepts/note',
... 'contentType': u'text/restructured',
... 'linkUrl': u'http://'})
>>> view = NodeView(m112, request) >>> view = NodeView(m112, request)
>>> cont = CreateObject(view, request) >>> cont = CreateObject(view, request)
>>> cont.update() >>> cont.update()
@ -802,7 +804,7 @@ The new technique uses the ``fields`` and ``data`` attributes...
linkText textline False None linkText textline False None
>>> view.data >>> view.data
{'linkUrl': u'http://', 'contentType': 'text/restructured', 'data': u'', {'linkUrl': u'http://', 'contentType': u'text/restructured', 'data': u'',
'linkText': u'', 'title': u'Test Note'} 'linkText': u'', 'title': u'Test Note'}
The object is changed via a FormController adapter created for The object is changed via a FormController adapter created for

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de # Copyright (c) 2015 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
""" """
Adapters and others classes for analyzing resources. Adapters and others classes for analyzing resources.
$Id$
""" """
from itertools import tee from itertools import tee
@ -41,6 +39,7 @@ from loops.resource import Resource
from loops.setup import addAndConfigureObject from loops.setup import addAndConfigureObject
from loops.type import TypeInterfaceSourceList from loops.type import TypeInterfaceSourceList
logger = getLogger('Classifier')
TypeInterfaceSourceList.typeInterfaces += (IClassifier,) TypeInterfaceSourceList.typeInterfaces += (IClassifier,)
@ -102,15 +101,15 @@ class Classifier(AdapterBase):
if resource not in resources: if resource not in resources:
concept.assignResource(resource, predicate) concept.assignResource(resource, predicate)
message = u'Assigning: %s %s %s' message = u'Assigning: %s %s %s'
self.log(message % (resource.title, predicate.title, concept.title), 5)
else: else:
message = u'Already assigned: %s %s %s' message = u'Already assigned: %s %s %s'
self.log(message % (resource.title, predicate.title, concept.title), 4) self.log(message % (resource.title, predicate.title, concept.title), 4)
def log(self, message, level=5): def log(self, message, level=5):
if level >= self.logLevel: if level >= self.logLevel:
#print 'Classifier %s:' % getName(self.context), message #print 'Classifier %s:' % getName(self.context), message
getLogger('Classifier').info( logger.info(u'%s: %s' % (getName(self.context), message))
u'%s: %s' % (getName(self.context), message))
class Extractor(object): class Extractor(object):

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de # Copyright (c) 2015 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,17 +18,20 @@
""" """
View class(es) for resource classifiers. View class(es) for resource classifiers.
$Id$
""" """
from logging import getLogger
import transaction
from zope import interface, component from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.traversing.api import getName
from loops.browser.concept import ConceptView from loops.browser.concept import ConceptView
from loops.common import adapted from loops.common import adapted
logger = getLogger('ClassifierView')
class ClassifierView(ConceptView): class ClassifierView(ConceptView):
@ -42,12 +45,18 @@ class ClassifierView(ConceptView):
if 'update' in self.request.form: if 'update' in self.request.form:
cta = adapted(self.context) cta = adapted(self.context)
if cta is not None: if cta is not None:
for r in collectResources(self.context): for idx, r in enumerate(collectResources(self.context)):
if idx % 1000 == 0:
logger.info('Committing, resource # %s' % idx)
transaction.commit()
cta.process(r) cta.process(r)
logger.info('Finished processing')
transaction.commit()
return True return True
def collectResources(concept, checkedConcepts=None, result=None): def collectResources(concept, checkedConcepts=None, result=None):
logger.info('Start collecting resources for %s' % getName(concept))
if result is None: if result is None:
result = [] result = []
if checkedConcepts is None: if checkedConcepts is None:
@ -59,4 +68,5 @@ def collectResources(concept, checkedConcepts=None, result=None):
if c not in checkedConcepts: if c not in checkedConcepts:
checkedConcepts.append(c) checkedConcepts.append(c)
collectResources(c, checkedConcepts, result) collectResources(c, checkedConcepts, result)
logger.info('Collected %s resources' % len(result))
return result return result

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: 0.13.1\n" "Project-Id-Version: 0.13.1\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n" "POT-Creation-Date: 2007-05-22 12:00 CET\n"
"PO-Revision-Date: 2015-06-06 12:00 CET\n" "PO-Revision-Date: 2015-09-22 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n" "Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n" "Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -597,9 +597,6 @@ msgstr "Informationen über dieses Objekt"
msgid "Information about this object." msgid "Information about this object."
msgstr "Informationen über dieses Objekt." msgstr "Informationen über dieses Objekt."
msgid "Send a link to this object by email."
msgstr "Einen Link zu diesem Objekt per E-Mail versenden."
msgid "Edit with external editor." msgid "Edit with external editor."
msgstr "Mit 'External Editor' bearbeiten." msgstr "Mit 'External Editor' bearbeiten."
@ -1416,3 +1413,27 @@ msgstr "Zeitraum"
msgid "Technology" msgid "Technology"
msgstr "Technik" msgstr "Technik"
# send mail
msgid "Send a link to this object by email."
msgstr "Einen Link zu diesem Objekt per E-Mail versenden."
msgid "Send Link by Email"
msgstr "Link per E-Mail versenden"
msgid "Mail Subject"
msgstr "Betreff"
msgid "Mail Body"
msgstr "Text"
msgid "Recipients"
msgstr "Empfänger"
msgid "Additional Recipients"
msgstr "Weitere Empfänger"
msgid "Send email"
msgstr "E-Mail senden"

View file

@ -175,6 +175,10 @@ class SendEmailForm(NodeView):
@Lazy @Lazy
def subject(self): def subject(self):
optionKey = 'organize.sendmail_subject'
option = self.globalOptions(optionKey) or self.typeOptions(optionKey)
if option:
return option[0]
menu = self.context.getMenu() menu = self.context.getMenu()
zdc = IZopeDublinCore(menu) zdc = IZopeDublinCore(menu)
zdc.languageInfo = self.languageInfo zdc.languageInfo = self.languageInfo
@ -185,6 +189,8 @@ class SendEmailForm(NodeView):
class SendEmail(FormController): class SendEmail(FormController):
bccToSender = False
def checkPermissions(self): def checkPermissions(self):
return (not self.isAnonymous and return (not self.isAnonymous and
super(SendEmail, self).checkPermissions()) super(SendEmail, self).checkPermissions())
@ -201,7 +207,10 @@ class SendEmail(FormController):
msg = MIMEText(message.encode('utf-8'), 'plain', 'utf-8') msg = MIMEText(message.encode('utf-8'), 'plain', 'utf-8')
msg['Subject'] = subject.encode('utf-8') msg['Subject'] = subject.encode('utf-8')
msg['From'] = sender msg['From'] = sender
msg['To'] = ', '.join(r.strip() for r in recipients if r.strip()) recipients = [r.strip() for r in recipients if r.strip()]
msg['To'] = ', '.join(recipients)
if self.bccToSender:
recipients.append(sender)
mailhost = component.getUtility(IMailDelivery, 'Mail') mailhost = component.getUtility(IMailDelivery, 'Mail')
mailhost.send(sender, recipients, msg.as_string()) mailhost.send(sender, recipients, msg.as_string())
return True return True

View file

@ -125,14 +125,15 @@
<span tal:content="view/target/title"></span></div> <span tal:content="view/target/title"></span></div>
<metal:content define-macro="mail_content"> <metal:content define-macro="mail_content">
<div> <div>
<label i18n:translate="" for="subject">Subject</label> <label i18n:translate="" for="subject">Mail Subject</label>
<div> <div>
<input name="subject" id="subject" style="width: 60em" <input name="subject" id="subject" style="width: 60em"
dojoType="dijit.form.ValidationTextBox" required dojoType="dijit.form.ValidationTextBox" required
tal:attributes="value view/subject" /></div> tal:attributes="value view/subject" /></div>
</div> </div>
<div> <div>
<label i18n:translate="" for="mailbody">Mail Body</label> <label i18n:translate=""
for="mailbody">Mail Body</label>
<div> <div>
<textarea name="mailbody" cols="80" rows="4" id="mailbody" <textarea name="mailbody" cols="80" rows="4" id="mailbody"
dojoType="dijit.form.SimpleTextarea" style="width: 60em" dojoType="dijit.form.SimpleTextarea" style="width: 60em"
@ -155,7 +156,8 @@
<span i18n:translate="">Toggle all</span></div> <span i18n:translate="">Toggle all</span></div>
</div> </div>
<div> <div>
<label i18n:translate="" for="addrecipients">Additional recipients</label> <label i18n:translate=""
for="addrecipients">Additional Recipients</label>
<div> <div>
<textarea name="addrecipients" cols="80" rows="4" id="addrecipients" <textarea name="addrecipients" cols="80" rows="4" id="addrecipients"
dojoType="dijit.form.SimpleTextarea" dojoType="dijit.form.SimpleTextarea"

View file

@ -177,11 +177,15 @@ Recent changes
>>> data[0].timeStamp >>> data[0].timeStamp
u'... ...:...' u'... ...:...'
>>> data[0].objectData >>> data[0].objectData
{'version': '', 'canAccess': True, 'title': 'Change Doc 001', 'url': '', {'version': '', 'description': u'', 'title': 'Change Doc 001', 'url': '',
'object': <loops.resource.Resource object at ...>, 'type': u'Text'} 'object': <loops.resource.Resource object at ...>, 'type': u'Text',
'canAccess': True}
>>> data[0].user >>> data[0].user
{'url': '', 'object': <loops.concept.Concept ...>, 'title': u'john'} {'url': '', 'object': <loops.concept.Concept ...>, 'title': u'john'}
>>> data[0].action >>> data[0].action
'modify' 'modify'

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2014 Helmut Merz helmutm@cy55.de # Copyright (c) 2015 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -265,20 +265,23 @@ class TrackDetails(BaseView):
def objectData(self): def objectData(self):
obj = self.object obj = self.object
if obj is None: if obj is None:
return dict(object=None, title='-', type='-', url='', return dict(object=None, title='-', description='',
type='-', url='',
version=None, canAccess=False) version=None, canAccess=False)
node = self.view.nodeView node = self.view.nodeView
url = node is not None and node.getUrlForTarget(obj) or '' url = node is not None and node.getUrlForTarget(obj) or ''
view = self.view.getViewForObject(obj) view = self.view.getViewForObject(obj)
if view is None: if view is None:
title = obj.title title = obj.title
desc = obj.description
else: else:
title = view.listingTitle title = view.listingTitle
desc = view.description
versionable = IVersionable(self.object, None) versionable = IVersionable(self.object, None)
version = ((versionable is not None and version = ((versionable is not None and
not (versionable.notVersioned) and not (versionable.notVersioned) and
versionable.versionId) or '') versionable.versionId) or '')
return dict(object=obj, title=title, return dict(object=obj, title=title, description=desc,
type=self.longTypeTitle, url=url, version=version, type=self.longTypeTitle, url=url, version=version,
canAccess=canAccessObject(obj)) canAccess=canAccessObject(obj))

View file

@ -27,7 +27,7 @@
<tr tal:define="wiType row/workItemType" <tr tal:define="wiType row/workItemType"
tal:attributes="class wiType/indicator|nothing"> tal:attributes="class wiType/indicator|nothing">
<td class="nowrap center" <td class="nowrap center"
tal:define="today python: row.isToday and ' today' or ''" tal:define="today python:row.isToday and ' today' or ''"
tal:attributes="title row/weekDay; tal:attributes="title row/weekDay;
class string:nowrap center$today" class string:nowrap center$today"
i18n:attributes="title" i18n:attributes="title"
@ -35,9 +35,10 @@
<td class="nowrap center" tal:content="row/start">17:30</td> <td class="nowrap center" tal:content="row/start">17:30</td>
<td class="nowrap center" tal:content="row/end">20:00</td> <td class="nowrap center" tal:content="row/end">20:00</td>
<td class="nowrap center" tal:content="row/duration">2:30</td> <td class="nowrap center" tal:content="row/duration">2:30</td>
<td tal:condition="python: 'Task' in work.columns"> <td tal:condition="python:'Task' in work.columns">
<a tal:define="data row/objectData" <a tal:define="data row/objectData"
tal:attributes="href data/url" tal:attributes="href data/url;
title data/description"
tal:content="data/title">Task</a></td> tal:content="data/title">Task</a></td>
<td tal:condition="python: 'User' in work.columns"> <td tal:condition="python: 'User' in work.columns">
<a tal:attributes="href row/user/url" <a tal:attributes="href row/user/url"

View file

@ -88,6 +88,13 @@ class DataTable(AdapterBase):
if data is None: if data is None:
data = OOBTree() data = OOBTree()
self.context._data = data self.context._data = data
reclen = len(self.columns) - 1
for k, v in data.items():
v = v[:reclen]
missing = reclen - len(v)
if missing > 0:
v += (missing * [u''])
data[k] = v
return data return data
def setData(self, data): def setData(self, data):
self.context._data = OOBTree(data) self.context._data = OOBTree(data)
@ -102,6 +109,7 @@ class DataTable(AdapterBase):
item[c] = k item[c] = k
else: else:
item[c] = v[idx-1] item[c] = v[idx-1]
#item[c] = len(v) > idx and v[idx-1] or u''
result.append(item) result.append(item)
return result return result