From 10bae7f32d5b9362f359cb0c4cc616287b5316aa Mon Sep 17 00:00:00 2001 From: helmutm Date: Thu, 13 Dec 2007 16:42:03 +0000 Subject: [PATCH] provide language switchingby top actions with flags git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2241 fd906abe-77d9-0310-91a1-e0d9ade77398 --- README.txt | 2 +- browser/common.py | 1 + browser/loops.css | 14 ++++++++++ browser/node.py | 3 +++ browser/node_macros.pt | 7 ++++- helpers.txt | 58 +++++++++++++++-------------------------- i18n/browser.py | 20 ++++++++++---- i18n/configure.zcml | 4 ++- i18n/flags/de.gif | Bin 0 -> 362 bytes i18n/flags/en.gif | Bin 0 -> 260 bytes i18n/flags/it.gif | Bin 0 -> 366 bytes i18n/flags/pl.gif | Bin 0 -> 360 bytes i18n/i18n_macros.pt | 14 ++++++++++ search/README.txt | 39 +++++++++------------------ tests/setup.py | 13 +++++++++ 15 files changed, 104 insertions(+), 71 deletions(-) create mode 100755 i18n/flags/de.gif create mode 100644 i18n/flags/en.gif create mode 100755 i18n/flags/it.gif create mode 100755 i18n/flags/pl.gif create mode 100644 i18n/i18n_macros.pt diff --git a/README.txt b/README.txt index 3938d8b..67a4ae7 100755 --- a/README.txt +++ b/README.txt @@ -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 diff --git a/browser/common.py b/browser/common.py index a56cb7a..9a00d5c 100644 --- a/browser/common.py +++ b/browser/common.py @@ -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 diff --git a/browser/loops.css b/browser/loops.css index ba29dc1..a01dd3b 100644 --- a/browser/loops.css +++ b/browser/loops.css @@ -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 */ diff --git a/browser/node.py b/browser/node.py index 632478e..75b3d8d 100644 --- a/browser/node.py +++ b/browser/node.py @@ -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): diff --git a/browser/node_macros.pt b/browser/node_macros.pt index f339b7b..0a53a44 100644 --- a/browser/node_macros.pt +++ b/browser/node_macros.pt @@ -200,7 +200,12 @@ - + + + + blubb + + diff --git a/helpers.txt b/helpers.txt index 2fe8b04..29d11f9 100755 --- a/helpers.txt +++ b/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 diff --git a/i18n/browser.py b/i18n/browser.py index 7f5e820..fbc632e 100644 --- a/i18n/browser.py +++ b/i18n/browser.py @@ -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() diff --git a/i18n/configure.zcml b/i18n/configure.zcml index 7a78860..539eb27 100644 --- a/i18n/configure.zcml +++ b/i18n/configure.zcml @@ -5,8 +5,10 @@ xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"> + + diff --git a/i18n/flags/de.gif b/i18n/flags/de.gif new file mode 100755 index 0000000000000000000000000000000000000000..75728ddf21520698e46ede37ffb6ffe6797b2eda GIT binary patch literal 362 zcmV-w0hRtoNk%w1VGsZd0M!5h@Z4^tKQH`sb@}U;sC_TBwX3LxFY`x7nTvh(rdIZ{ zX_SeDRmk~Z$xI*esy^4v)C;Ck|#M6-%7SXfx=i6{ABV78<$X*oGRKRWhOQtvY}y1KFV zSy{b7F`I-h{MDq&xUcenaD!W0=FJ`I9v)^%Jm`WQ@8E5%t)rE4a)5z>`|+ZjoRH|x zB0oPbA^8LV00000EC2ui01yBR000Juz>Q<*Nd1n;q=UsQ6mn1oqt{z3e#_l%GHG~8 zq|GLU_}O9-iB9+V%pPzQs_d!=1Q?2`r8*oeD-bdPb$EJwet-~ygcW&veSd)vHG>2& z3JM9EA|f3fB_%fs0Vg~e85$ZOARZnZ96mHSFDC=H0};9*ya2uc7bC(W1Ox`hF9a9L I7Z*VQJGrf#Gynhq literal 0 HcmV?d00001 diff --git a/i18n/flags/en.gif b/i18n/flags/en.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c6bce15c44106c2df5c7a50f43ace1ed95bf047 GIT binary patch literal 260 zcmV+f0sH<(Nk%w1VGsZd0K@^ZuCMA~Rr!T{VS~Ynna}=XVtbp!|Lf{JQmlm=H*ti=+u`)AVSnMi-(+*O z!o=O#-R=JV|NsC0A^8LV00000EC2ui01yBR000GnV5Ce?*=1Q51Qm2lOygk;;}L`x zMcji75JjW$iF_oI&!s{5FcJ_(MDzF@0Y^%qa!GVN)-V7Y{&)%*0&>hYxZMEDxBPL3 z5%_H+P@p93bqfgr0u>A&4+I-^1_%`h1P&1b10D|?7Z)C52rC>24jU5(0STrB78V5p KtO)}NApkqQ-f7za literal 0 HcmV?d00001 diff --git a/i18n/flags/it.gif b/i18n/flags/it.gif new file mode 100755 index 0000000000000000000000000000000000000000..d79e90e99e9cd5e9603313cd6a840bdf23abd8f2 GIT binary patch literal 366 zcmV-!0g?VkNk%w1VGsZd0M!5h_e@Lxg#hB>;@;lg<^TZl6BGGWRrK`q^*uf8008nX zE?Bx*09OF<4GsSO{#?CW^8f%tuR>I|RRDbed&+wNWdKyURP`k#^BEaWwod?d0P`|3 z-~a#si2%aF!uK&TNwi7yA0PHBD|yIybjEb$<>dfx005By^foqFyjac6&GsxT$jHb` zvrG6lH#(|1Xu@dvTwL=tHTFbAV83D8+uQZ^_1ypf{r&y>`}_R-{PyU|{hL z4gLK6@(>XEMMd{DHTN$s_9`mw008wQB<27B;s5~jA0PQTI_dxb`$|dkGc)rU8Q%Z^ z_4W1oKR^BP@$wWD`dwZ3Pfz$$RQyj*_vz{TWo7T-;q^m9{d9En=H~tH@&1mE>Dk%q z-QD@?>-+8P`)FwVYHID@-|^$)`}_O&`1sub0Qvd(_V)Jr`uhC*{P*|w{r&y_@$vrt z{{R2~A^8LV00000EC2ui01yBR000Jsz@D(@Q2_ui7b%sRnPk1+&qw9)bUIq@R@=3r zRG&4w?c#Qg~;C6SYrmzfL-1{(x~hKLO(0s$0JO|I|& literal 0 HcmV?d00001 diff --git a/i18n/i18n_macros.pt b/i18n/i18n_macros.pt new file mode 100644 index 0000000..30cfe2d --- /dev/null +++ b/i18n/i18n_macros.pt @@ -0,0 +1,14 @@ + + + + + + + diff --git a/search/README.txt b/search/README.txt index 97491e7..9768427 100755 --- a/search/README.txt +++ b/search/README.txt @@ -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 diff --git a/tests/setup.py b/tests/setup.py index 34056eb..11fcc36 100644 --- a/tests/setup.py +++ b/tests/setup.py @@ -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)