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 0000000..75728dd
Binary files /dev/null and b/i18n/flags/de.gif differ
diff --git a/i18n/flags/en.gif b/i18n/flags/en.gif
new file mode 100644
index 0000000..3c6bce1
Binary files /dev/null and b/i18n/flags/en.gif differ
diff --git a/i18n/flags/it.gif b/i18n/flags/it.gif
new file mode 100755
index 0000000..d79e90e
Binary files /dev/null and b/i18n/flags/it.gif differ
diff --git a/i18n/flags/pl.gif b/i18n/flags/pl.gif
new file mode 100755
index 0000000..bf10646
Binary files /dev/null and b/i18n/flags/pl.gif differ
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)