=============================================================== loops - Linked Objects for Organization and Processing Services =============================================================== >>> from zope import component >>> from zope.traversing.api import getName Let's set up a loops site with basic and example concepts and resources. >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown >>> site = placefulSetUp(True) >>> from loops.organize.setup import SetupManager >>> component.provideAdapter(SetupManager, name='organize') >>> from loops.tests.setup import TestSite >>> t = TestSite(site) >>> concepts, resources, views = t.setup() >>> loopsRoot = site['loops'] Compund Objects - Hierarchies with Ordered Components ===================================================== >>> from loops.compound.base import Compound >>> component.provideAdapter(Compound) >>> tType = concepts.getTypeConcept() >>> from loops.setup import addAndConfigureObject >>> from loops.concept import Concept >>> from loops.compound.interfaces import ICompound We first create the compound type and one instance of the newly created type. We also need an ``ispartof`` predicate. >>> tCompound = addAndConfigureObject(concepts, Concept, 'compound', ... title=u'Compound', ... conceptType=tType, typeInterface=ICompound) >>> c01 = addAndConfigureObject(concepts, Concept, 'c01', ... title=u'Compound #01', conceptType=tCompound) >>> tPredicate = concepts.getPredicateType() >>> isPartof = addAndConfigureObject(concepts, Concept, 'ispartof', ... title=u'is Part of', conceptType=tPredicate) In order to access the compound concept's attributes we have to adapt it. >>> from loops.common import adapted >>> aC01 = adapted(c01) Now we are able to add resources to it. >>> aC01.add(resources[u'd003.txt']) >>> aC01.add(resources[u'd001.txt']) >>> [getName(p) for p in aC01.getParts()] [u'd003.txt', u'd001.txt'] >>> aC01.add(resources[u'd001.txt'], 0) >>> [getName(p) for p in aC01.getParts()] [u'd001.txt', u'd003.txt', u'd001.txt'] >>> aC01.add(resources[u'd002.txt'], -1) >>> [getName(p) for p in aC01.getParts()] [u'd001.txt', u'd003.txt', u'd002.txt', u'd001.txt'] We can reorder the parts of a compound. >>> aC01.reorder([resources[u'd002.txt'], resources[u'd001.txt'], ]) >>> [getName(p) for p in aC01.getParts()] [u'd002.txt', u'd001.txt', u'd003.txt', u'd001.txt'] And remove a part from the compound. >>> aC01.remove(resources[u'd001.txt'], 1) >>> [getName(p) for p in aC01.getParts()] [u'd002.txt', u'd003.txt', u'd001.txt'] Blogs ===== >>> from loops.compound.blog.post import BlogPost >>> from loops.compound.blog.interfaces import IBlogPost >>> component.provideAdapter(BlogPost, provides=IBlogPost) >>> tBlog = addAndConfigureObject(concepts, Concept, 'blog', title=u'Blog', ... conceptType=tType) >>> tBlogPost = addAndConfigureObject(concepts, Concept, 'blogpost', ... title=u'Blog Post', conceptType=tType, ... typeInterface=IBlogPost) >>> myBlog = addAndConfigureObject(concepts, Concept, 'myblog', title=u'My Blog', ... conceptType=tBlog) >>> firstPost = addAndConfigureObject(concepts, Concept, 'firstpost', ... title=u'My first post', conceptType=tBlogPost) >>> aFirstPost = adapted(firstPost) >>> aFirstPost.date >>> aFirstPost.text = u'My first blog post.' >>> aFirstPost.text u'My first blog post.' >>> aFirstPost.creator Blog and BlogPost views ----------------------- >>> from loops.compound.blog.browser import BlogView, BlogPostView >>> #from zope.publisher.browser import TestRequest >>> from loops.tests.auth import TestRequest The blog view automatically provides a portlet action for creating a new post. >>> view = BlogView(myBlog, TestRequest()) >>> for act in view.getActions('portlet'): ... print act.name createBlogPost >>> view = BlogPostView(firstPost, TestRequest()) >>> data = view.data Automatic assignment of a blog post to the personal blog of its owner --------------------------------------------------------------------- As all the following stuff relies on a blog being assigned to a person we need the corresponding scaffolding from the loops.organize package. >>> from loops.organize.tests import setupUtilitiesAndAdapters >>> setupData = setupUtilitiesAndAdapters(loopsRoot) We also have to set up some components for automatic setting of security properties upon object creation. >>> from loops.security.common import setDefaultSecurity >>> component.provideHandler(setDefaultSecurity) >>> from loops.compound.blog.security import BlogPostSecuritySetter >>> component.provideAdapter(BlogPostSecuritySetter) Let's start with defining a user (more precisely: a principal) and a corresponding person. >>> auth = setupData.auth >>> tPerson = concepts['person'] >>> userJohn = auth.definePrincipal('users.john', u'John', login='john') >>> persJohn = addAndConfigureObject(concepts, Concept, 'person.john', ... title=u'John Smith', conceptType=tPerson, ... userId='users.john') >>> blogJohn = addAndConfigureObject(concepts, Concept, 'blog.john', ... title=u'John\'s Blog', conceptType=tBlog) >>> persJohn.assignChild(blogJohn) Let's now login as the newly defined user. >>> from loops.tests.auth import login >>> login(userJohn) Let's also provide some general permission settings. These are necessary as after logging in the permissions of the user will be checked by the standard checker defined in the test setup. >>> grantPermission = setupData.rolePermissions.grantPermissionToRole >>> assignRole = setupData.principalRoles.assignRoleToPrincipal >>> grantPermission('zope.View', 'zope.Member') >>> grantPermission('zope.View', 'loops.Owner') >>> grantPermission('zope.ManageContent', 'zope.ContentManager') >>> grantPermission('loops.ViewRestricted', 'loops.Owner') >>> assignRole('zope.Member', 'users.john') The automatic assignment of the blog post is done in the form controller used for creating the blog post. >>> home = views['home'] >>> home.target = myBlog >>> from loops.compound.blog.browser import CreateBlogPostForm, CreateBlogPost >>> input = {'title': u'John\'s first post', 'text': u'Text of John\'s post', ... 'date': '2008-02-02T15:54:11', ... 'privateComment': u'John\'s private comment', ... 'form.type': '.loops/concepts/blogpost'} >>> cbpForm = CreateBlogPostForm(home, TestRequest(form=input)) >>> cbpController = CreateBlogPost(cbpForm, cbpForm.request) >>> cbpController.update() False >>> posts = blogJohn.getChildren() >>> len(posts) 1 >>> postJohn0 = posts[0] >>> postJohn0.title u"John's first post" >>> postJohn0Text = adapted(postJohn0.getResources()[0]) >>> postJohn0Text.data u"Text of John's post" Security ======== We first have to define some checkers that will be invoked when checking access to attributes. >>> from zope.security.checker import Checker, defineChecker >>> checker = Checker(dict(title='zope.View', privateComment='loops.ViewRestricted'), ... dict(title='zope.ManageContent', ... privateComment='zope.ManageContent')) >>> #defineChecker(Concept, checker) >>> defineChecker(BlogPost, checker) >>> from loops.resource import Resource, TextDocumentAdapter >>> checker = Checker(dict(title='zope.View', data='zope.View'), ... dict(title='zope.ManageContent', data='zope.ManageContent')) >>> #defineChecker(Resource, checker) >>> defineChecker(TextDocumentAdapter, checker) Standard security settings for blogs ------------------------------------ TODO... A personal blog is a blog that is a direct child of a person with an associated principal (i.e. a user id). Blog posts in a personal blog can only be created by the owner of the blog. More generally: A personal blog may receive only blog posts as children that have the same owner as the blog itself. A personal blog may only be assigned to other parents by the owner of the blog. Standard security settings for blog posts ----------------------------------------- Blog posts may (only!) be edited by their owner (i.e. only the owner has the ManageContent permission). (TODO: Also their parent assignments may be changed only by the owner). Note that we still are logged-in as user John. >>> from zope.security import canAccess, canWrite, checkPermission >>> canAccess(postJohn0, 'title') True >>> canWrite(postJohn0, 'title') True This settings are also valid for children/resources that are assigned via an `is Part of` relation. >>> canAccess(postJohn0Text, 'data') True >>> canWrite(postJohn0Text, 'data') True The private comment is only visible (and editable, of course) for the owner of the blog post. >>> aPostJohn0 = adapted(postJohn0) >>> canAccess(aPostJohn0, 'privateComment') True >>> canWrite(aPostJohn0, 'privateComment') True So let's now switch to another user. On a global level, Martha also has the ContentManager role, i.e. she is allowed to edit content objects. Nevertheless she is not allowed to change John's blog post. >>> userMartha = auth.definePrincipal('users.martha', u'Martha', login='martha') >>> assignRole('zope.Member', 'users.martha') >>> assignRole('zope.ContentManager', 'users.martha') >>> login(userMartha) >>> canAccess(postJohn0, 'title') True >>> canWrite(postJohn0, 'title') False >>> canAccess(postJohn0Text, 'data') True >>> canWrite(postJohn0Text, 'data') False >>> canAccess(aPostJohn0, 'privateComment') False >>> canWrite(aPostJohn0, 'privateComment') False A blog post marked as private is only visible for its owner. >>> login(userJohn) >>> aPostJohn0.private = True >>> canAccess(postJohn0, 'title') True >>> canAccess(postJohn0Text, 'data') True >>> login(userMartha) >>> canAccess(postJohn0, 'data') False >>> canAccess(postJohn0Text, 'data') False When we clear the `private` flag the post becomes visible again. >>> aPostJohn0.private = False >>> canAccess(postJohn0, 'title') True >>> canAccess(postJohn0Text, 'title') True Micro Articles ============== >>> from loops.compound.microart.base import MicroArt >>> from loops.compound.microart.interfaces import IMicroArt >>> component.provideAdapter(MicroArt, provides=IMicroArt) >>> tMicroArt = addAndConfigureObject(concepts, Concept, 'microart', ... title=u'MicroArt', conceptType=tType, ... typeInterface=IMicroArt) >>> ma01 = addAndConfigureObject(concepts, Concept, 'ma01', ... conceptType=tMicroArt, ... title=u'Organizational Knowledge', ... story=u'Systemic KM talks about organizational knowledge.', ... insight=u'Organizational knowledge is not visible.', ... consequences=u'Use examples. Look for strucure and rules. ' ... u'Knowledge shows itself in actions.', ... followUps=u'What about collective intelligence? ' ... u'How does an organization express itself?') >>> ma01._insight u'Organizational knowledge is not visible.' >>> list(resources) [..., u'ma01_story'] >>> adMa01 = adapted(ma01) >>> adMa01.insight u'Organizational knowledge is not visible.' >>> adMa01.story u'Systemic KM talks about organizational knowledge.' Books, Sections, and Pages ========================== >>> import os >>> from loops.setup import importData >>> importPath = os.path.join(os.path.dirname(__file__), 'book') >>> importData(loopsRoot, importPath, 'loops_book_de.dmp') >>> from loops.compound.book.browser import BookView, SectionView, TopicView Fin de partie ============= >>> placefulTearDown()