merge branch master
This commit is contained in:
commit
d50498bdaf
17 changed files with 114 additions and 40 deletions
|
@ -413,7 +413,7 @@ class ConceptView(BaseView):
|
|||
if r.order != pos:
|
||||
r.order = pos
|
||||
|
||||
def resources(self):
|
||||
def getResources(self):
|
||||
form = self.request.form
|
||||
#if form.get('loops.viewName') == 'index.html' and self.editable:
|
||||
if self.editable:
|
||||
|
@ -430,6 +430,9 @@ class ConceptView(BaseView):
|
|||
if fv.check(r.first):
|
||||
yield ResourceRelationView(r, self.request, contextIsSecond=True)
|
||||
|
||||
def resources(self):
|
||||
return self.getResources()
|
||||
|
||||
@Lazy
|
||||
def resourcesList(self):
|
||||
from loops.browser.resource import ResourceRelationView
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -98,7 +98,7 @@ class AdapterBase(object):
|
|||
|
||||
adapts(IConcept)
|
||||
|
||||
_adapterAttributes = ('context', '__parent__')
|
||||
_adapterAttributes = ('context', '__parent__', 'request')
|
||||
_contextAttributes = list(IConcept)
|
||||
_noexportAttributes = ()
|
||||
_textIndexAttributes = ()
|
||||
|
@ -106,6 +106,7 @@ class AdapterBase(object):
|
|||
__is_dummy__ = False
|
||||
__type__ = None
|
||||
|
||||
request = None
|
||||
languageInfo = None
|
||||
|
||||
def __init__(self, context):
|
||||
|
|
|
@ -77,6 +77,30 @@ class SectionView(Base, ConceptView):
|
|||
def sectionType(self):
|
||||
return self.conceptManager['section']
|
||||
|
||||
def getResources(self):
|
||||
relViews = super(SectionView, self).getResources()
|
||||
return relViews
|
||||
|
||||
@Lazy
|
||||
def textResources(self):
|
||||
self.images = [[]]
|
||||
result = []
|
||||
idx = 0
|
||||
for rv in self.getResources():
|
||||
if rv.context.contentType.startswith('text/'):
|
||||
idx += 1
|
||||
result.append(rv)
|
||||
self.images.append([])
|
||||
else:
|
||||
self.registerDojoLightbox()
|
||||
url = self.nodeView.getUrlForTarget(rv.context)
|
||||
src = '%s/mediaasset.html?v=small' % url
|
||||
fullSrc = '%s/mediaasset.html?v=medium' % url
|
||||
img = dict(src=src, fullImageUrl=fullSrc, title=rv.title,
|
||||
description=rv.description, url=url, object=rv)
|
||||
self.images[idx].append(img)
|
||||
return result
|
||||
|
||||
def getCssClassForResource(self, r):
|
||||
for c in r.context.getConcepts([self.defaultPredicate]):
|
||||
if c.conceptType == self.documentTypeType:
|
||||
|
|
|
@ -21,6 +21,7 @@ concept(u'issubtype', u'is Subtype', u'predicate', options=u'hide_children',
|
|||
# document types
|
||||
concept(u'keyquestions', u'Leitfragen', u'documenttype')
|
||||
concept(u'textelement', u'Textabschnitt', u'documenttype')
|
||||
concept(u'textelement2', u'Textabschnitt separat', u'documenttype')
|
||||
concept(u'quote', u'Zitat', u'documenttype')
|
||||
concept(u'story', u'Geschichte', u'documenttype')
|
||||
concept(u'usecase', u'Fallbeispiel', u'documenttype')
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<metal:section define-macro="section">
|
||||
<metal:info use-macro="view/concept_macros/concepttitle" />
|
||||
<div tal:repeat="related item/resources">
|
||||
<div tal:repeat="related item/textResources">
|
||||
<div class="span-4">
|
||||
<div tal:attributes="class python:
|
||||
item.getCssClassForResource(related)"
|
||||
|
@ -36,7 +36,8 @@
|
|||
string:$resourceBase/cybertools.icons/vcard_edit.png" /></a>
|
||||
<a i18n:translate="" i18n:attributes="title"
|
||||
title="Edit with external editor."
|
||||
tal:condition="related/xeditable"
|
||||
xxtal:condition="related/xeditable"
|
||||
tal:condition="nothing"
|
||||
tal:attributes="href string:$url/external_edit?version=this">
|
||||
<img tal:attributes="src
|
||||
string:$resourceBase/cybertools.icons/application_edit.png" /></a>
|
||||
|
@ -45,6 +46,15 @@
|
|||
<a tal:content="parent/title"
|
||||
tal:attributes="href python:view.getUrlForTarget(parent)" />
|
||||
</div>
|
||||
<div tal:repeat="image python:
|
||||
item.images[repeat['related'].index() + 1]">
|
||||
<a dojoType="dojox.image.Lightbox" group="mediasset"
|
||||
i18n:attributes="title"
|
||||
tal:attributes="href image/fullImageUrl;
|
||||
title image/title">
|
||||
<img tal:attributes="src image/src;
|
||||
alt image/title" /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</metal:section>
|
||||
|
|
|
@ -231,7 +231,7 @@ Extracting Document Properties from MS Office Files
|
|||
>>> path = os.path.join(dataDir, 'office')
|
||||
>>> fn = os.path.join(path, 'example.docx')
|
||||
>>> os.path.getsize(fn)
|
||||
20337...
|
||||
23561...
|
||||
|
||||
>>> officeFile = addAndConfigureObject(resources, Resource, 'test.docx',
|
||||
... title=u'Example Word File', resourceType=tOfficeFile,
|
||||
|
@ -241,7 +241,7 @@ Extracting Document Properties from MS Office Files
|
|||
|
||||
>>> content = aOfficeFile.data
|
||||
>>> len(content)
|
||||
17409
|
||||
20327
|
||||
|
||||
Clean up:
|
||||
>>> shutil.copy(fn + '.sav', fn)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -18,8 +18,6 @@
|
|||
|
||||
"""
|
||||
View class(es) for integrating external objects.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope import interface, component
|
||||
|
@ -42,7 +40,9 @@ class ExternalCollectionView(ConceptView):
|
|||
def update(self):
|
||||
if 'update' in self.request.form:
|
||||
cta = adapted(self.context)
|
||||
if cta is not None:
|
||||
cta.request = self.request
|
||||
cta.update()
|
||||
if cta.updateMessage is not None:
|
||||
self.request.form['message'] = cta.updateMessage
|
||||
return True
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -19,8 +19,6 @@
|
|||
"""
|
||||
Concept adapter(s) for external collections, e.g. a directory in the
|
||||
file system.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
@ -62,10 +60,12 @@ class ExternalCollectionAdapter(AdapterBase):
|
|||
implements(IExternalCollection)
|
||||
adapts(IConcept)
|
||||
|
||||
_adapterAttributes = ('context', '__parent__', 'exclude', 'newResources')
|
||||
_adapterAttributes = AdapterBase._adapterAttributes + (
|
||||
'exclude', 'newResources', 'updateMessage')
|
||||
_contextAttributes = list(IExternalCollection) + list(IConcept)
|
||||
|
||||
newResources = None
|
||||
updateMessage = None
|
||||
|
||||
def getExclude(self):
|
||||
return getattr(self.context, '_exclude', None) or []
|
||||
|
@ -101,12 +101,17 @@ class ExternalCollectionAdapter(AdapterBase):
|
|||
adobj = adapted(obj)
|
||||
directory = provider.getDirectory(self)
|
||||
adobj.storageParams=dict(subdirectory=directory)
|
||||
adobj.request = self.request
|
||||
adobj.externalAddress = addr
|
||||
# collect error information
|
||||
if adobj.processingErrors:
|
||||
message = self.updateMessage or u''
|
||||
message += u'<br />'.join(adobj.processingErrors)
|
||||
self.updateMessage = message
|
||||
# force reindexing
|
||||
notify(ObjectModifiedEvent(obj))
|
||||
else:
|
||||
new.append(addr)
|
||||
#print '*** new', new
|
||||
if new:
|
||||
self.newResources = provider.createExtFileObjects(self, new)
|
||||
for r in self.newResources:
|
||||
|
@ -205,7 +210,13 @@ class DirectoryCollectionProvider(object):
|
|||
contentType=contentType,
|
||||
)
|
||||
adobj = adapted(obj)
|
||||
adobj.request = client.request
|
||||
adobj.externalAddress = addr # must be set last
|
||||
# collect error information
|
||||
if adobj.processingErrors:
|
||||
message = client.updateMessage or u''
|
||||
message += u'<br />'.join(adobj.processingErrors)
|
||||
client.updateMessage = message
|
||||
yield obj
|
||||
|
||||
def getDirectory(self, client):
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
<metal:block use-macro="view/concept_macros/conceptdata">
|
||||
<metal:fill tal:condition="item/editable"
|
||||
fill-slot="fields">
|
||||
<div class="error"
|
||||
tal:define="message view/message | request/message | nothing"
|
||||
tal:condition="message"
|
||||
tal:content="structure message" />
|
||||
<metal:block use-macro="view/concept_macros/conceptfields" />
|
||||
<form method="post">
|
||||
<input type="submit" name="update" value="Update Collection" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -18,8 +18,6 @@
|
|||
|
||||
"""
|
||||
Resource adapter(s) for MS Office files.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
|
@ -56,6 +54,7 @@ class OfficeFile(ExternalFileAdapter):
|
|||
|
||||
propertyMap = {u'Revision:': 'version'}
|
||||
propFileName = 'docProps/custom.xml'
|
||||
corePropFileName = 'docProps/core.xml'
|
||||
fileExtensions = ('.docm', '.docx', 'dotm', 'dotx', 'pptx', 'potx', 'ppsx',
|
||||
'.xlsm', '.xlsx', '.xltm', '.xltx')
|
||||
|
||||
|
@ -88,20 +87,32 @@ class OfficeFile(ExternalFileAdapter):
|
|||
from logging import getLogger
|
||||
self.logger.warn(e)
|
||||
return []
|
||||
if self.corePropFileName not in zf.namelist():
|
||||
self.logger.warn('Core properties not found in file %s.' %
|
||||
self.externalAddress)
|
||||
if self.propFileName not in zf.namelist():
|
||||
self.logger.warn('Custom properties not found in file %s.' %
|
||||
self.externalAddress)
|
||||
propsXml = zf.read(self.propFileName)
|
||||
corePropsXml = zf.read(self.corePropFileName)
|
||||
# TODO: read core.xml, return both trees in dictionary
|
||||
zf.close()
|
||||
return etree.fromstring(propsXml)
|
||||
return {'custom': etree.fromstring(propsXml),
|
||||
'core': etree.fromstring(corePropsXml)}
|
||||
|
||||
def getDocProperty(self, pname):
|
||||
for p in self.docPropertyDom:
|
||||
for p in self.docPropertyDom['custom']:
|
||||
name = p.attrib.get('name')
|
||||
if name == pname:
|
||||
return p[0].text
|
||||
return None
|
||||
|
||||
def getCoreProperty(self, pname):
|
||||
for p in self.docPropertyDom['core']:
|
||||
if p.tag.endswith(pname):
|
||||
return p.text
|
||||
return None
|
||||
|
||||
def processDocument(self):
|
||||
changed = False
|
||||
docVersion = None
|
||||
|
@ -109,11 +120,14 @@ class OfficeFile(ExternalFileAdapter):
|
|||
strType = ('{http://schemas.openxmlformats.org/'
|
||||
'officeDocument/2006/docPropsVTypes}lpwstr')
|
||||
attributes = {}
|
||||
dom = self.docPropertyDom
|
||||
# get dc:description from core.xml
|
||||
desc = self.getCoreProperty('description')
|
||||
if desc is not None:
|
||||
attributes['comments'] = desc
|
||||
dom = self.docPropertyDom['custom']
|
||||
for p in dom:
|
||||
name = p.attrib.get('name')
|
||||
value = p[0].text
|
||||
#print '***', self.externalAddress, name, value, p[0].tag
|
||||
attr = self.propertyMap.get(name)
|
||||
if attr == 'version':
|
||||
docVersion = value
|
||||
|
@ -136,7 +150,9 @@ class OfficeFile(ExternalFileAdapter):
|
|||
newZf.writestr(self.propFileName, etree.tostring(dom))
|
||||
newZf.close()
|
||||
shutil.move(newFn, fn)
|
||||
self.update(attributes)
|
||||
errors = self.update(attributes)
|
||||
if errors:
|
||||
self.processingErrors = errors
|
||||
|
||||
def update(self, attributes):
|
||||
# to be implemented by subclass
|
||||
|
@ -146,10 +162,10 @@ class OfficeFile(ExternalFileAdapter):
|
|||
def parseDate(s):
|
||||
if not s:
|
||||
return None
|
||||
try:
|
||||
tt = strptime(s, '%Y-%m-%dT%H:%M:%SZ')
|
||||
#try:
|
||||
# tt = strptime(s, '%Y-%m-%dT%H:%M:%SZ')
|
||||
#except ValueError:
|
||||
except ValueError:
|
||||
return None
|
||||
# try:
|
||||
# tt = strptime(s, '%d.%m.%y')
|
||||
# except ValueError:
|
||||
|
|
BIN
integrator/testdata/office/example.docx
vendored
BIN
integrator/testdata/office/example.docx
vendored
Binary file not shown.
BIN
integrator/testdata/office/example.docx.sav
vendored
BIN
integrator/testdata/office/example.docx.sav
vendored
Binary file not shown.
Binary file not shown.
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
|
||||
"Project-Id-Version: $Id$\n"
|
||||
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
|
||||
"PO-Revision-Date: 2012-08-20 12:00 CET\n"
|
||||
"PO-Revision-Date: 2012-09-17 12:00 CET\n"
|
||||
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
|
||||
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -758,6 +758,9 @@ msgstr "Kommentare"
|
|||
msgid "Add Comment"
|
||||
msgstr "Kommentar hinzufügen"
|
||||
|
||||
msgid "Subject"
|
||||
msgstr "Thema"
|
||||
|
||||
msgid "Selection using: $targets"
|
||||
msgstr "Auswahl über: $targets"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -18,8 +18,6 @@
|
|||
|
||||
"""
|
||||
Definition of view classes and other browser related stuff for comments.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope import interface, component
|
||||
|
@ -52,6 +50,8 @@ class CommentsView(NodeView):
|
|||
|
||||
@Lazy
|
||||
def allowed(self):
|
||||
if self.isAnonymous:
|
||||
return False
|
||||
return (self.virtualTargetObject is not None and
|
||||
self.globalOptions('organize.allowComments'))
|
||||
|
||||
|
@ -120,8 +120,8 @@ class CreateComment(EditObject):
|
|||
def update(self):
|
||||
form = self.request.form
|
||||
subject = form.get('subject')
|
||||
text = form.get('text')
|
||||
if not subject or not text or self.personId is None or self.object is None:
|
||||
text = form.get('text') or u''
|
||||
if not subject or self.personId is None or self.object is None:
|
||||
return True
|
||||
#contentType = form.get('contentType') or 'text/restructured'
|
||||
rm = self.view.loopsRoot.getRecordManager()
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
</div>
|
||||
<div class="buttons">
|
||||
<input value="Save" type="submit"
|
||||
onClick="return closeDialog(true)"
|
||||
onClick="if (not this.validate()) return False; return closeDialog(true)"
|
||||
i18n:attributes="value">
|
||||
<input type="button" value="Cancel"
|
||||
onClick="return closeDialog(false)"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# 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
|
||||
|
@ -18,8 +18,6 @@
|
|||
|
||||
"""
|
||||
Definition of the Concept class.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from cStringIO import StringIO
|
||||
|
@ -352,7 +350,10 @@ class ExternalFileAdapter(FileAdapter):
|
|||
implements(IExternalFile)
|
||||
|
||||
_adapterAttributes = (FileAdapter._adapterAttributes
|
||||
+ ('storageParams', 'externalAddress', 'uniqueAddress'))
|
||||
+ ('storageParams', 'externalAddress', 'uniqueAddress',
|
||||
'processingErrors'))
|
||||
|
||||
processingErrors = []
|
||||
|
||||
def getStorageParams(self):
|
||||
params = getattr(self.context, '_storageParams', None)
|
||||
|
|
Loading…
Add table
Reference in a new issue