provide language switchingby top actions with flags
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2241 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
49bb669053
commit
10bae7f32d
15 changed files with 104 additions and 71 deletions
|
@ -4,7 +4,7 @@ loops - Linked Objects for Organization and Processing Services
|
|||
|
||||
($Id$)
|
||||
|
||||
The loops platform is built up of three parts:
|
||||
The loops platform consists up of three basic types of objects:
|
||||
|
||||
(1) concepts: simple interconnected objects usually representing
|
||||
meta-information
|
||||
|
|
|
@ -106,6 +106,7 @@ class BaseView(GenericView, I18NView):
|
|||
# TODO: get rid of removeSecurityProxy() call
|
||||
self.context = removeSecurityProxy(context)
|
||||
self.setSkin(self.loopsRoot.skinName)
|
||||
self.checkLanguage()
|
||||
try:
|
||||
if not canAccess(context, 'title'):
|
||||
raise Unauthorized
|
||||
|
|
|
@ -31,6 +31,12 @@ table.listing td {
|
|||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.top-actions{
|
||||
position: absolute;
|
||||
right: 2em;
|
||||
top: 1em;
|
||||
}
|
||||
|
||||
/*.content-1 h1 { */
|
||||
h1 {
|
||||
font-size: 160%;
|
||||
|
@ -98,6 +104,14 @@ div.image {
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
img.selected {
|
||||
border: 2px solid #d6dcf6;
|
||||
}
|
||||
|
||||
img.notselected {
|
||||
border: 2px solid #eff8ff;
|
||||
}
|
||||
|
||||
|
||||
/* search stuff */
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ from cybertools.browser import configurator
|
|||
from cybertools.browser.view import GenericView
|
||||
from cybertools.typology.interfaces import IType, ITypeManager
|
||||
from cybertools.xedit.browser import ExternalEditorView
|
||||
from loops.i18n.browser import i18n_macros
|
||||
from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
|
||||
from loops.interfaces import IViewConfiguratorSchema
|
||||
from loops.resource import MediaAsset
|
||||
|
@ -77,6 +78,8 @@ class NodeView(BaseView):
|
|||
media='all', position=3)
|
||||
cm.register('js', 'loops.js', resourceName='loops.js')
|
||||
#cm.register('js', 'loops.js', resourceName='loops1.js')
|
||||
cm.register('top_actions', 'top_actions', name='multi_actions',
|
||||
subMacros=[i18n_macros.macros['language_switch']])
|
||||
cm.register('portlet_left', 'navigation', title='Navigation',
|
||||
subMacro=node_macros.macros['menu'])
|
||||
#if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
|
||||
|
|
|
@ -200,7 +200,12 @@
|
|||
</metal:menu>
|
||||
|
||||
|
||||
<!-- portlets -->
|
||||
<!-- portlets and actions -->
|
||||
|
||||
<metal:actions define-macro="top_actions">
|
||||
blubb
|
||||
</metal:actions>
|
||||
|
||||
|
||||
<metal:actions define-macro="clipboard">
|
||||
<div class="menu-2">loops Development</div>
|
||||
|
|
58
helpers.txt
58
helpers.txt
|
@ -10,39 +10,22 @@ package.
|
|||
|
||||
Let's first do some basic imports
|
||||
|
||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||
>>> site = placefulSetUp(True)
|
||||
|
||||
>>> from zope import interface, component
|
||||
>>> from zope.interface import Interface
|
||||
>>> from zope.publisher.browser import TestRequest
|
||||
|
||||
and provide a relation registry:
|
||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||
>>> site = placefulSetUp(True)
|
||||
|
||||
>>> from cybertools.relation.tests import IntIdsStub
|
||||
>>> component.provideUtility(IntIdsStub())
|
||||
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||
>>> component.provideUtility(DummyRelationRegistry())
|
||||
>>> from loops.tests.setup import TestSite
|
||||
>>> t = TestSite(site)
|
||||
>>> concepts, resources, views = t.setup()
|
||||
|
||||
and care for some type adapter machinery:
|
||||
|
||||
>>> from loops.interfaces import IConcept
|
||||
>>> from loops.interfaces import ITypeConcept
|
||||
>>> from loops.type import TypeConcept
|
||||
>>> component.provideAdapter(TypeConcept, (IConcept,), ITypeConcept)
|
||||
|
||||
Now we can setup a simple loops site with its manager objects, using a
|
||||
loops setup manager:
|
||||
|
||||
>>> from loops.base import Loops
|
||||
>>> loopsRoot = site['loops'] = Loops()
|
||||
|
||||
>>> from loops.setup import SetupManager
|
||||
>>> setup = SetupManager(loopsRoot)
|
||||
>>> concepts, resources, views = setup.setup()
|
||||
>>> concepts['hasType'].title
|
||||
u'has Type'
|
||||
|
||||
>>> loopsRoot = site['loops']
|
||||
|
||||
We also add some example concepts,
|
||||
|
||||
>>> from loops.concept import Concept
|
||||
|
@ -217,6 +200,8 @@ for controlling e.g. storage for external files.
|
|||
>>> ef1_type = IType(ef1)
|
||||
>>> IType(ef1).options
|
||||
[]
|
||||
|
||||
>>> from loops.type import TypeConcept
|
||||
>>> extfile_ad = TypeConcept(extfile)
|
||||
>>> extfile_ad.options = ['dummy', 'storage:varsubdir',
|
||||
... 'storage_parameters:extfiles']
|
||||
|
@ -239,11 +224,9 @@ get a type manager from all loops objects, always with the same context:
|
|||
True
|
||||
|
||||
>>> types = typeManager.types
|
||||
>>> sorted(t.token for t in types)
|
||||
['.loops/concepts/domain', '.loops/concepts/file', '.loops/concepts/note',
|
||||
'.loops/concepts/predicate', '.loops/concepts/query',
|
||||
'.loops/concepts/textdocument', '.loops/concepts/topic',
|
||||
'.loops/concepts/type']
|
||||
>>> typeTokens = sorted(t.token for t in types)
|
||||
>>> len(typeTokens)
|
||||
9
|
||||
|
||||
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
|
||||
True
|
||||
|
@ -252,14 +235,13 @@ The listTypes() method allows us to select types that fulfill a certain
|
|||
condition:
|
||||
|
||||
>>> types = typeManager.listTypes(include=('concept',))
|
||||
>>> sorted(t.token for t in types)
|
||||
['.loops/concepts/domain', '.loops/concepts/predicate',
|
||||
'.loops/concepts/query', '.loops/concepts/topic', '.loops/concepts/type']
|
||||
>>> typeTokens = sorted(t.token for t in types)
|
||||
>>> len(typeTokens)
|
||||
6
|
||||
>>> types = typeManager.listTypes(exclude=('concept',))
|
||||
>>> sorted(t.token for t in types)
|
||||
['.loops/concepts/file', '.loops/concepts/note',
|
||||
'.loops/concepts/textdocument']
|
||||
|
||||
>>> typeTokens = sorted(t.token for t in types)
|
||||
>>> len(typeTokens)
|
||||
3
|
||||
|
||||
Type-based interfaces and adapters
|
||||
----------------------------------
|
||||
|
@ -273,6 +255,7 @@ will be adaptable to. The default for this is None:
|
|||
For concept objects that provide types (type providers) the value of
|
||||
the typeInterface attribute is the ITypeConcept interface:
|
||||
|
||||
>>> from loops.interfaces import ITypeConcept
|
||||
>>> topic_type.typeInterface is ITypeConcept
|
||||
True
|
||||
|
||||
|
@ -286,6 +269,7 @@ i.e. the 'topic' concept, via an adapter:
|
|||
>>> class Topic(object):
|
||||
... implements(ITopic)
|
||||
... def __init__(self, context): pass
|
||||
>>> from loops.interfaces import IConcept
|
||||
>>> component.provideAdapter(Topic, (IConcept,), ITopic)
|
||||
|
||||
>>> ITypeConcept(topic).typeInterface = ITopic
|
||||
|
@ -306,7 +290,7 @@ browser views; among others also some important informations about the
|
|||
context object's type:
|
||||
|
||||
>>> from loops.browser.common import BaseView
|
||||
>>> view = BaseView(cc1, TestRequest)
|
||||
>>> view = BaseView(cc1, TestRequest())
|
||||
>>> view.typeTitle
|
||||
u'Topic'
|
||||
>>> view.typeInterface
|
||||
|
|
|
@ -24,6 +24,7 @@ $Id$
|
|||
|
||||
from zope import interface, component
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.app.session.interfaces import ISession
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.i18n.interfaces import IUserPreferredLanguages
|
||||
from zope.i18n.negotiator import negotiator
|
||||
|
@ -31,6 +32,11 @@ from zope.i18n.negotiator import negotiator
|
|||
from loops.common import adapted
|
||||
|
||||
|
||||
packageId = 'loops.i18n.browser'
|
||||
|
||||
i18n_macros = ViewPageTemplateFile('i18n_macros.pt')
|
||||
|
||||
|
||||
class LanguageInfo(object):
|
||||
|
||||
def __init__(self, context, request):
|
||||
|
@ -80,8 +86,10 @@ class I18NView(object):
|
|||
return adapted(self.context, self.languageInfo)
|
||||
|
||||
def checkLanguage(self):
|
||||
# get language from session
|
||||
self.setPreferredLanguage()
|
||||
session = ISession(self.request)[packageId]
|
||||
lang = session.get('language')
|
||||
if lang:
|
||||
self.setLanguage(lang)
|
||||
|
||||
def setLanguage(self, lang=None):
|
||||
lang = lang or self.request.form.get('lang')
|
||||
|
@ -89,10 +97,12 @@ class I18NView(object):
|
|||
upl = IUserPreferredLanguages(self.request)
|
||||
upl.setPreferredLanguages([lang])
|
||||
|
||||
def switchLanguage(self, lang=None, keep=False):
|
||||
def switchLanguage(self):
|
||||
lang = self.request.form.get('loops.language')
|
||||
keep = self.request.form.get('keep')
|
||||
if keep:
|
||||
pass # set in session
|
||||
self.setPreferredLanguage(lang)
|
||||
session = ISession(self.request)[packageId]
|
||||
session['language'] = lang
|
||||
self.setLanguage(lang)
|
||||
return self()
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
xmlns="http://namespaces.zope.org/browser"
|
||||
i18n_domain="zope">
|
||||
|
||||
<resourceDirectory name="i18n.flags" directory="flags" />
|
||||
|
||||
<page for="loops.interfaces.INode"
|
||||
name="language_switch"
|
||||
name="switch_language"
|
||||
class="loops.browser.node.NodeView"
|
||||
attribute="switchLanguage"
|
||||
permission="zope.Public" />
|
||||
|
|
BIN
i18n/flags/de.gif
Executable file
BIN
i18n/flags/de.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 362 B |
BIN
i18n/flags/en.gif
Normal file
BIN
i18n/flags/en.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 260 B |
BIN
i18n/flags/it.gif
Executable file
BIN
i18n/flags/it.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 366 B |
BIN
i18n/flags/pl.gif
Executable file
BIN
i18n/flags/pl.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 360 B |
14
i18n/i18n_macros.pt
Normal file
14
i18n/i18n_macros.pt
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!-- $Id$ -->
|
||||
|
||||
<metal:actions define-macro="language_switch"
|
||||
tal:define="langInfo view/languageInfo">
|
||||
<tal:lang repeat="lang langInfo/availableLanguages">
|
||||
<a href="#"
|
||||
tal:attributes="href string:switch_language?loops.language=$lang&keep=yes;
|
||||
title lang"><img src="us.gif"
|
||||
tal:attributes="src string:${resourceBase}i18n.flags/$lang.gif;
|
||||
title lang; alt lang;
|
||||
class python: langInfo.language == lang
|
||||
and 'selected' or 'notselected'" /></a>
|
||||
</tal:lang>
|
||||
</metal:actions>
|
|
@ -17,29 +17,17 @@ and setup a simple loops site with a concept manager and some concepts
|
|||
(with all the type machinery, what in real life is done via standard
|
||||
ZCML setup):
|
||||
|
||||
>>> from cybertools.relation.registry import DummyRelationRegistry
|
||||
>>> from cybertools.relation.tests import IntIdsStub
|
||||
>>> relations = DummyRelationRegistry()
|
||||
>>> component.provideUtility(relations)
|
||||
>>> component.provideUtility(IntIdsStub())
|
||||
|
||||
>>> from loops.concept import Concept
|
||||
>>> from loops.type import ConceptType, TypeConcept
|
||||
>>> from loops.interfaces import ITypeConcept
|
||||
>>> component.provideAdapter(ConceptType)
|
||||
>>> component.provideAdapter(TypeConcept)
|
||||
|
||||
>>> from loops.base import Loops
|
||||
>>> loopsRoot = site['loops'] = Loops()
|
||||
>>> from loops.tests.setup import TestSite
|
||||
>>> t = TestSite(site)
|
||||
>>> concepts, resources, views = t.setup()
|
||||
|
||||
>>> from loops.setup import SetupManager
|
||||
>>> setup = SetupManager(loopsRoot)
|
||||
>>> concepts, resources, views = setup.setup()
|
||||
>>> typeConcept = concepts['type']
|
||||
|
||||
>>> from loops.concept import Concept
|
||||
>>> topic = concepts['topic'] = Concept(u'Topic')
|
||||
>>> for c in (topic,): c.conceptType = typeConcept
|
||||
>>> loopsRoot = site['loops']
|
||||
>>> query = concepts['query']
|
||||
>>> topic = concepts['topic'] = Concept(u'Topic')
|
||||
|
||||
In addition we create a concept that holds the search page and a node
|
||||
(page) that links to this concept:
|
||||
|
@ -103,7 +91,7 @@ a controller attribute for the search view.
|
|||
|
||||
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
|
||||
'return submitReplacing("1.results", "1.search.form",
|
||||
"http://127.0.0.1/loops/views/page/.target10/@@searchresults.html")'
|
||||
"http://127.0.0.1/loops/views/page/.target29/@@searchresults.html")'
|
||||
|
||||
Basic (text/title) search
|
||||
-------------------------
|
||||
|
@ -202,7 +190,7 @@ of the concepts' titles:
|
|||
>>> request = TestRequest(form=form)
|
||||
>>> view = Search(page, request)
|
||||
>>> view.listConcepts()
|
||||
"[['Zope (Topic)', '12']]"
|
||||
"[['Zope (Topic)', '33']]"
|
||||
|
||||
Preset Concept Types on Search Forms
|
||||
------------------------------------
|
||||
|
@ -213,8 +201,7 @@ a certain qualifier, via the option attribute of the type interface.
|
|||
|
||||
Let's start with a new type, the customer type.
|
||||
|
||||
>>> customer = concepts['customer'] = Concept('Customer')
|
||||
>>> customer.conceptType = typeConcept
|
||||
>>> customer = concepts['customer']
|
||||
>>> custType = ITypeConcept(customer)
|
||||
>>> custType.options
|
||||
[]
|
||||
|
@ -239,16 +226,16 @@ and thus include the customer type in the preset search types.
|
|||
('concept', 'search')
|
||||
>>> searchView = Search(search, TestRequest())
|
||||
>>> list(searchView.presetSearchTypes)
|
||||
[{'token': 'loops:concept:customer', 'title': 'Customer'}]
|
||||
[{'token': 'loops:concept:customer', 'title': u'Customer'}]
|
||||
|
||||
>>> searchView.conceptsForType('loops:concept:customer')
|
||||
[{'token': 'none', 'title': u'not selected'},
|
||||
{'token': '18', 'title': u'Zope Corporation'},
|
||||
{'token': '19', 'title': u'cyberconcepts'}]
|
||||
{'token': '47', 'title': u'Zope Corporation'},
|
||||
{'token': '49', 'title': u'cyberconcepts'}]
|
||||
|
||||
Let's use this new search option for querying:
|
||||
|
||||
>>> form = {'search.4.text_selected': u'18'}
|
||||
>>> form = {'search.4.text_selected': u'47'}
|
||||
>>> resultsView = SearchResults(page, TestRequest(form=form))
|
||||
>>> results = list(resultsView.results)
|
||||
>>> results[0].title
|
||||
|
|
|
@ -13,8 +13,11 @@ from zope.app.catalog.text import TextIndex
|
|||
from zope.app.container.interfaces import IObjectRemovedEvent
|
||||
from zope.app.security.principalregistry import principalRegistry
|
||||
from zope.app.security.interfaces import IAuthentication
|
||||
from zope.app.session.interfaces import IClientIdManager, ISessionDataContainer
|
||||
from zope.app.session import session
|
||||
from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter
|
||||
from zope.dublincore.interfaces import IZopeDublinCore
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.composer.schema.factory import SchemaFactory
|
||||
from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
|
||||
|
@ -40,6 +43,12 @@ from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFac
|
|||
from loops.setup import SetupManager, addObject
|
||||
from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept
|
||||
|
||||
class ClientIdManager(object):
|
||||
""" dummy, for testing only """
|
||||
implements(IClientIdManager)
|
||||
def getClientId(self, request):
|
||||
return 'dummy'
|
||||
|
||||
|
||||
class TestSite(object):
|
||||
|
||||
|
@ -58,6 +67,10 @@ class TestSite(object):
|
|||
component.provideAdapter(ZDCAnnotatableAdapter, (ILoopsObject,), IZopeDublinCore)
|
||||
component.provideAdapter(AttributeAnnotations, (ILoopsObject,))
|
||||
component.provideUtility(principalRegistry, IAuthentication)
|
||||
component.provideAdapter(session.ClientId)
|
||||
component.provideAdapter(session.Session)
|
||||
component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer)
|
||||
component.provideUtility(ClientIdManager())
|
||||
|
||||
component.provideAdapter(LoopsType)
|
||||
component.provideAdapter(ConceptType)
|
||||
|
|
Loading…
Add table
Reference in a new issue