merge branch master

This commit is contained in:
Helmut Merz 2013-11-16 13:19:01 +01:00
commit 3a332b368c
23 changed files with 208 additions and 91 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@
*.pydevproject
*.sublime-project
*.sublime-workspace
.settings

View file

@ -52,6 +52,7 @@ from zope.traversing.browser import absoluteURL
from zope.traversing.api import getName, getParent, traverse
from cybertools.ajax.dojo import dojoMacroTemplate
from cybertools.browser.action import actions
from cybertools.browser.view import GenericView
from cybertools.meta.interfaces import IOptions
from cybertools.meta.element import Element
@ -617,7 +618,6 @@ class BaseView(GenericView, I18NView):
for opt in (self.options, self.typeOptions, self.globalOptions):
if isinstance(opt, DummyOptions):
continue
#import pdb; pdb.set_trace()
v = opt
for key in keys.split('.'):
if isinstance(v, list):
@ -706,22 +706,37 @@ class BaseView(GenericView, I18NView):
@Lazy
def states(self):
return self.getStates()
@Lazy
def allStates(self):
return self.getStates(False)
def getStates(self, forDisplay=True):
result = []
if not checkPermission(self.viewStatesPermission, self.context):
if forDisplay and not checkPermission(self.viewStatesPermission, self.context):
# do not display state information
return result
if IResource.providedBy(self.target):
statesDefs = self.globalOptions('organize.stateful.resource', ())
statesDefs = (self.globalOptions('organize.stateful.resource') or [])
else:
typeOptions = self.typeOptions('organize.stateful')
if typeOptions is None:
typeOptions = []
statesDefs = (self.globalOptions('organize.stateful.concept', []) +
typeOptions)
statesDefs = (self.globalOptions('organize.stateful.concept') or [])
statesDefs += (self.typeOptions('organize.stateful') or [])
for std in statesDefs:
stf = component.getAdapter(self.target, IStateful, name=std)
result.append(stf)
return result
def checkState(self):
if not self.allStates:
return True
for stf in self.allStates:
option = self.globalOptions(
'organize.stateful.restrict.' + stf.statesDefinition)
if option:
return stf.state in option
return True
# controlling actions and editing
@Lazy
@ -732,10 +747,21 @@ class BaseView(GenericView, I18NView):
""" Return a list of actions that provide the view and edit actions
available for the context object.
"""
actions = []
acts = []
optKey = 'action.' + category
actNames = (self.options(optKey) or []) + (self.typeOptions(optKey) or [])
if actNames:
acts = list(actions.get(category, actNames,
view=self, page=page, target=target))
if category in self.actions:
actions.extend(self.actions[category](self, page=page, target=target))
return actions
acts.extend(self.actions[category](self, page, target))
optKey = 'append_action.' + category
actNames = (self.options(optKey) or []) + (self.typeOptions(optKey) or [])
if actNames:
acts.extend(list(actions.get(category, actNames,
view=self, page=page, target=target)))
return acts
def getAdditionalActions(self, category='object', page=None, target=None):
""" Provide additional actions; override by subclass.

View file

@ -535,17 +535,6 @@ class ConceptView(BaseView):
for node in self.context.getClients():
yield NodeView(node, self.request)
def getActions(self, category='object', page=None, target=None):
acts = []
optKey = 'action.' + category
actNames = (self.options(optKey) or []) + (self.typeOptions(optKey) or [])
if actNames:
acts = list(actions.get(category, actNames,
view=self, page=page, target=target))
if category in self.actions:
acts.extend(self.actions[category](self, page, target))
return acts
def getPortletActions(self, page=None, target=None):
if self.portlet_actions:
return actions.get('portlet', self.portlet_actions,

View file

@ -64,10 +64,12 @@
</h1>
<metal:block use-macro="view/concept_macros/filter_input" />
</metal:title>
<p metal:define-macro="conceptdescription"
<metal:desc define-macro="conceptdescription"
tal:define="description description|item/renderedDescription"
tal:condition="description">
<i tal:content="structure description">Description</i></p>
<div class="description"
tal:content="structure description">Description</div>
</metal:desc>
</metal:title>
@ -130,7 +132,8 @@
<metal:children define-macro="conceptchildren">
<div tal:attributes="class string:content-$level;
ondblclick python: item.openEditWindow('configure.html')"
tal:define="children python: list(item.unique(item.children()))"
tal:define="list_nested request/list_nested|nothing;
children children|python:list(item.unique(item.children()))"
tal:condition="children">
<h2 i18n:translate=""
tal:condition="show_headline|python:True">Children</h2>
@ -163,7 +166,7 @@
<td valign="top">
<a tal:attributes="href python: view.getUrlForTarget(related);
title related/relationInfo">
<span tal:replace="related/title">Resource Title</span>
<span tal:replace="related/title">Concept Title</span>
</a>
</td>
<td class="center"><span tal:content="related/typeTitle"
@ -180,6 +183,14 @@
tal:attributes="value related/uidToken" />
</td>
</tr>
<tr tal:define="children python:list(related.unique(related.children()));
resources python:list(related.resources())"
tal:condition="python:list_nested and (children or resources)">
<td tal:condition="item/showCheckboxes|nothing" />
<td colspan="5">
<metal:list use-macro="item/template/macros/list_nested" />
</td>
</tr>
</tal:item>
</tal:items>
</tbody>
@ -321,6 +332,20 @@
</metal:listing>
<metal:listing define-macro="list_nested">
<div style="margin-left: 20px"
tal:define="item nocall:related;
level python:level + 1">
<tal:children condition="children">
<metal:list use-macro="item/template/macros/conceptchildren" />
</tal:children>
<tal:resources condition="resources">
<metal:list use-macro="item/template/macros/conceptresources" />
</tal:resources>
</div>
</metal:listing>
<!-- portlets -->
<metal:actions define-macro="parents">

0
browser/form.py Normal file → Executable file
View file

View file

@ -318,7 +318,8 @@
<metal:buttons define-slot="buttons">
<input value="Save" type="submit"
i18n:attributes="value"
tal:attributes="onClick python: view.closeAction(True) or
tal:attributes="value view/label_submit | string:Save;
onClick python: view.closeAction(True) or
'submit();; return false'">
<input type="button" value="Cancel" onClick="dlg.hide();"
i18n:attributes="value"

View file

@ -72,7 +72,8 @@
</tal:image>
<div tal:condition="cell/renderedTextDescription"
tal:attributes="class python:part.cssClass[1]">
<span tal:content="structure cell/renderedTextDescription" />
<span class="description"
tal:content="structure cell/renderedTextDescription" />
</div>
<tal:break condition="python:part.showImage and cell.img">
<br style="clear: both" />&nbsp;</tal:break>

View file

@ -319,7 +319,8 @@
i18n:translate="">Log in</a></div>
<div tal:define="register python:view.globalOptions('provideLogin')"
tal:condition="register">
<a tal:attributes="href python:register[0]"
<a tal:condition="python:register != True"
tal:attributes="href python:register[0]"
i18n:translate="">Register new member</a></div>
</metal:login>

View file

@ -47,7 +47,7 @@ from loops.browser.common import EditForm, BaseView
from loops.browser.concept import BaseRelationView, ConceptRelationView
from loops.browser.concept import ConceptConfigureView
from loops.browser.node import NodeView, node_macros
from loops.common import adapted, NameChooser
from loops.common import adapted, NameChooser, normalizeName
from loops.interfaces import IBaseResource, IDocument, ITextDocument
from loops.interfaces import IMediaAsset as legacy_IMediaAsset
from loops.interfaces import ITypeConcept
@ -214,7 +214,8 @@ class ResourceView(BaseView):
if self.typeOptions('no_normalize_download_filename'):
filename = '"%s"' % filename
else:
filename = NameChooser(getParent(self.context)).normalizeName(filename)
#filename = NameChooser(getParent(self.context)).normalizeName(filename)
filename = normalizeName(filename)
response.setHeader('Content-Disposition',
'attachment; filename=%s' % filename)
response.setHeader('Content-Length', len(data))

View file

@ -12,9 +12,11 @@
<h1><a tal:omit-tag="python: level > 1"
tal:attributes="href request/URL"
tal:content="item/title">Title</a></h1>
<p tal:define="description description|item/renderedDescription"
tal:condition="description">
<i tal:content="structure description">Description</i></p>
<tal:desc define="description description|item/renderedDescription"
condition="description">
<div class="description"
tal:content="structure description">Description</div>
</tal:desc>
<metal:fields define-slot="fields" />
<div class="content-1" id="1.body"
tal:attributes="id id;"
@ -53,7 +55,8 @@
tal:content="item/title">Title</a></h1><br />
<img tal:attributes="src
string:${view/url}/.${view/targetId}/view?version=this" />
<p><i tal:content="structure item/renderedDescription">Description</i></p>
<div class="description"
tal:content="structure item/renderedDescription">Description</div>
<metal:fields use-macro="view/comment_macros/comments" />
</div>
</metal:block>
@ -63,8 +66,10 @@
<div tal:attributes="ondblclick python: item.openEditWindow('edit.html')">
<div metal:use-macro="views/node_macros/object_actions" />
<h1 tal:content="item/title">Title</h1>
<p><i tal:content="structure item/renderedDescription">Description</i>&nbsp;</p>
<p>
<div class="description"
tal:content="structure item/renderedDescription">Description</div>
<br />
<div>
<span class="button">
<a i18n:translate=""
tal:attributes="href
@ -90,7 +95,7 @@
Open for editing
</a>
</span>
</p>
</div>
<metal:fields use-macro="view/comment_macros/comments" />
</div>
</metal:block>

View file

@ -22,10 +22,6 @@ body {
margin-top: 1em;
}
.head-description, .legend {
font-style: italic;
}
ul.view-modes {
padding: 0 0 0 2em;
margin: 0.7em 0 0 0;
@ -118,12 +114,23 @@ thead th {
padding-left: 0;
}
.head-description{
/* font-style: italic; */
font-size: 110%;
}
.description {
font-style: italic;
/* font-style: italic; */
/* margin-top: 0.5em;*/
font-size: 110%;
margin-bottom: 0.3em;
}
.legend {
/* font-style: italic; */
font-size: 95%;
}
.infotext {
font-size: 90%;
}
@ -302,6 +309,10 @@ fieldset.box td {
margin-top: -1px;
}
.nested {
margin-left: 2em;
}
.content-1 h1, h1 {
padding-top: 0.5em;
font-size: 180%;

View file

@ -257,7 +257,7 @@ def normalizeName(baseName):
except UnicodeDecodeError:
result.append('_')
continue
if c in '._':
if c in '._-':
# separator and special characters to keep
result.append(c)
continue

View file

@ -95,6 +95,11 @@ class Base(object):
if self.editable:
return 'index.html'
def children(self):
for c in self.getChildren():
if c.checkState():
yield c
def getResources(self):
relViews = super(Base, self).getResources()
return relViews
@ -106,6 +111,7 @@ class Base(object):
idx = 0
for rv in self.getResources():
if rv.context.contentType.startswith('text/'):
if rv.checkState():
idx += 1
result.append(rv)
self.images.append([])
@ -130,7 +136,8 @@ class Base(object):
return IOptions(adapted(dt))(name)
def getTitleForResource(self, r):
if self.getOptionsForResource(r, 'showtitle'):
if (IOptions(adapted(r.context.resourceType))('show_title_in_section') or
self.getOptionsForResource(r, 'show_title_in_section')):
return r.title
def getIconForResource(self, r):
@ -152,7 +159,9 @@ class Base(object):
def getParentsForResource(self, r):
for c in r.context.getConcepts([self.defaultPredicate]):
if c != self.context and c.conceptType != self.documentTypeType:
if (c != self.context and
c.conceptType != self.documentTypeType and
self.getViewForObject(c).checkState()):
yield c

View file

@ -1,18 +1,42 @@
<html i18n:domain="loops">
<metal:children define-macro="children">
<div tal:repeat="related item/children"
tal:define="level python:level + 1"
<metal:children define-macro="children"
tal:define="children children|python:list(item.children())">
<tal:child repeat="related children">
<div tal:define="children python:list(related.children());
resources python:list(related.resources());
hideEmpty python:item.getOptions('hide_empty_children');
level python:level + 1"
tal:condition="python:not hideEmpty or children or resources"
tal:attributes="class string:content-$level">
<h3><a tal:attributes="href python:view.getUrlForTarget(related)"
tal:content="related/title" />
</h3>
<div class="description"
tal:content="structure related/renderedDescription" />
<tal:nested condition="python:related.getOptions('show_nested_children')">
<metal:children use-macro="item/book_macros/nested_children" />
</tal:nested>
</div>
</tal:child>
</metal:children>
<metal:nested define-macro="nested_children"
tal:define="children children|python:list(item.children())">
<tal:child repeat="related children">
<div tal:define="level python:level + 1"
tal:attributes="class string:nested content-$level">
<h3>
<a tal:attributes="href python:view.getUrlForTarget(related)"
tal:content="related/title" />
</h3>
<div tal:content="structure related/renderedDescription" />
<!-- TODO: show next level (+/-) -->
</div>
</metal:children>
</tal:child>
</metal:nested>
<metal:book define-macro="book">
<metal:info use-macro="view/concept_macros/concepttitle" />
@ -30,7 +54,8 @@
tal:attributes="href pred/targetUrl;
title pred/title">
<img src="/@@/cybertools.icons/arrow_left.png" /></a>
<a tal:attributes="href parent/targetUrl;
<a tal:condition="nocall:parent"
tal:attributes="href parent/targetUrl;
title parent/title">
<img src="/@@/cybertools.icons/arrow_up.png" /></a>
<a tal:condition="nocall:succ"
@ -103,15 +128,17 @@
</metal:section>
<metal:topic define-macro="topic">
<metal:topic define-macro="topic"
tal:define="children children|python:list(item.children());
textResources textResources|item/textResources">
<metal:info use-macro="view/concept_macros/concepttitle" />
<h2 i18n:translate=""
tal:condition="python: list(item.children())">Children</h2>
tal:condition="children">Children</h2>
<metal:children use-macro="item/book_macros/children" />
<h2 i18n:translate=""
tal:condition="item/textResources">Text Elements</h2>
tal:condition="textResources">Text Elements</h2>
<div>
<div tal:repeat="related item/textResources"
<div tal:repeat="related textResources"
tal:define="level python:level + 1"
tal:attributes="class string:content-$level">
<h3>

View file

@ -70,7 +70,9 @@ class QuickSearchResults(NodeView):
fv = FilterView(self.context, self.request)
result = fv.apply(result)
result.sort(key=lambda x: x.title.lower())
return self.viewIterator(result)
for v in self.viewIterator(result):
if v.checkState():
yield v
class Search(ConceptView):
@ -257,8 +259,6 @@ class Search(ConceptView):
return self.viewIterator(result)
def checkStates(self, obj):
if not IResource.providedBy(obj):
return True
for std, states in self.selectedStates.items():
if std.startswith('state.resource.'):
std = std[len('state.resource.'):]

View file

@ -12,6 +12,8 @@
<zope:class class="loops.knowledge.survey.base.Questionnaire">
<require permission="zope.View"
interface="loops.knowledge.survey.interfaces.IQuestionnaire" />
<require permission="zope.View"
attributes="context" />
<require permission="zope.ManageContent"
set_schema="loops.knowledge.survey.interfaces.IQuestionnaire" />
</zope:class>
@ -23,6 +25,8 @@
<zope:class class="loops.knowledge.survey.base.QuestionGroup">
<require permission="zope.View"
interface="loops.knowledge.survey.interfaces.IQuestionGroup" />
<require permission="zope.View"
attributes="context" />
<require permission="zope.ManageContent"
set_schema="loops.knowledge.survey.interfaces.IQuestionGroup" />
</zope:class>
@ -34,6 +38,8 @@
<zope:class class="loops.knowledge.survey.base.Question">
<require permission="zope.View"
interface="loops.knowledge.survey.interfaces.IQuestion" />
<require permission="zope.View"
attributes="context" />
<require permission="zope.ManageContent"
set_schema="loops.knowledge.survey.interfaces.IQuestion" />
</zope:class>
@ -45,6 +51,8 @@
<zope:class class="loops.knowledge.survey.base.FeedbackItem">
<require permission="zope.View"
interface="loops.knowledge.survey.interfaces.IFeedbackItem" />
<require permission="zope.View"
attributes="context" />
<require permission="zope.ManageContent"
set_schema="loops.knowledge.survey.interfaces.IFeedbackItem" />
</zope:class>

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 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 @@
"""
Base classes for layout-based views.
$Id$
"""
from zope.app.security.interfaces import IUnauthenticatedPrincipal
@ -29,9 +27,11 @@ from zope.proxy import removeAllProxies
from zope.security.proxy import removeSecurityProxy
from zope.traversing.browser import absoluteURL
from cybertools.meta.interfaces import IOptions
from cybertools.util import format
from loops.common import adapted
from loops.common import adapted, baseObject
from loops.i18n.browser import LanguageInfo
from loops.browser.concept import ConceptView as BaseConceptView
from loops.browser.util import normalizeForUrl as normalize
from loops import util
@ -74,6 +74,10 @@ class BaseView(object):
def virtualTargetView(self):
return self.viewAnnotations.get('targetView')
@Lazy
def baseConceptView(self):
return BaseConceptView(baseObject(self.context), self.request)
@Lazy
def node(self):
return self.viewAnnotations.get('node')
@ -170,3 +174,7 @@ class BaseView(object):
def getMetaDescription(self):
return self.context.title
@Lazy
def globalOptions(self):
return IOptions(self.loopsRoot)

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 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 @@
"""
Layout node views.
$Id$
"""
from zope.app.security.interfaces import IUnauthenticatedPrincipal
@ -63,9 +61,12 @@ class LayoutNodeView(Page, BaseView):
@Lazy
def headTitle(self):
parts = [self.context.title]
if self.target is not None:
targetView = component.getMultiAdapter((self.target, self.request),
name='layout')
return ' - '.join((self.context.title, targetView.title))
else:
return self.context.title
if targetView.title not in parts:
parts.append(targetView.title)
if self.globalOptions('reverseHeadTitle'):
parts.reverse()
return ' - '.join(parts)

View file

@ -11,7 +11,8 @@
tal:attributes="src
string:${url}/@@mediaasset.html?version=this&v=medium" /></a>
</p>
<p><i tal:content="structure item/renderedDescription">Description</i></p>
<div class="description"
tal:content="structure item/renderedDescription">Description</div>
<metal:fields use-macro="view/comment_macros/comments" />
</div>
</metal:block>

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 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
@ -20,17 +20,16 @@
Views for displaying media assets.
Authors: Johann Schimpf, Erich Seifert.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.security.interfaces import Unauthorized
from zope.traversing.api import getParent
from loops.browser.node import NodeView
from loops.browser.resource import ResourceView, resource_macros
from loops.common import adapted
from loops.common import adapted, normalizeName
from loops.util import _
from loops import util
@ -62,7 +61,7 @@ class MediaAssetView(ResourceView):
if useAttachment:
filename = obj.localFilename or getName(self.context)
#filename = urllib.quote(filename)
filename = NameChooser(getParent(self.context)).normalizeName(filename)
filename = normalizeName(filename)
response.setHeader('Content-Disposition',
'attachment; filename=%s' % filename)
return data

View file

@ -112,7 +112,8 @@ class BaseMemberRegistration(NodeView):
@Lazy
def macro(self):
return schema_macros.macros['form']
#return schema_macros.macros['form']
return organize_macros.macros['register']
def checkPermissions(self):
personType = adapted(self.conceptManager['person'])

View file

@ -253,7 +253,8 @@ class BaseWorkItemsView(object):
tsTo += 3600 * 24 - 1 # include full end date
if tsFrom or tsTo:
result['timeFromTo'] = (tsFrom, tsTo)
state = form.get('wi_state') or self.options.wi_state
state = (form.get('wi_state') or
self.options.wi_state or self.typeOptions.wi_state)
if not state:
result['state'] = ['planned', 'accepted', 'running', 'done',
'done_x', 'finished', 'delegated', 'moved', 'cancelled']

View file

@ -11,7 +11,8 @@
<li><a tal:attributes="href row/url"
tal:content="row/title">My Site</a>
<tal:description condition="row/description"><br />
<i tal:content="structure row/renderedDescription" />
<div class="description"
tal:content="structure row/renderedDescription" />
</tal:description>
</li>
</tal:site>