Skip to content

Latest commit

 

History

History
199 lines (131 loc) · 7.24 KB

user_generated_content.rst

File metadata and controls

199 lines (131 loc) · 7.24 KB

User generated content

Get the code!

Get the code for this chapter (:doc:`More info <sneak>`) using this command in the buildout-directory:

cp -R src/ploneconf.site_sneak/chapters/26_user_generated_content/ src/ploneconf.site

How do prospective speakers submit talks? We let them register on the site and grant right to create talks. For this we go back to changing the site through-the-web.

In this chapter we:

  • allow self-registration
  • constrain types on the talk-folder
  • grant local roles
  • create a custom workflow for talks

Self-registration

Constrain types

  • On the talk-folder select Restrictions… from the Add new menu. Only allow to add talks.

Grant local roles

  • Go to Sharing and grant the role Can add to the group logged-in users. Now every user can add content in this folder (and only this folder).

Now all logged-in users can create and submit talks in this folder with the permission of the default-workflow.

A custom workflow for talks

We still need to fix a problem: Authenticated users can see all talks, even the ones of other users in the private state. Since we don't want this we will create a modified workflow for talks. The new workflow will only let them see and edit talks they created themselves and not the ones of other users.

  • Go to the ZMI > portal_workflow
  • See how talks have the same workflow as most content (Default)
  • Go to the tab Contents, check the box next to simple_publication_workflow, click copy and paste.
  • Rename the new workflow from copy_of_simple_publication_workflow to talks_workflow.
  • Edit the workflow by clicking on it: Change the Title to Talks Workflow.
  • Click on the tab States and click on private to edit this state. In the next view select the tab Permissions.
  • Find the talbe-column for the role Contributor and remove the permissions for Access contents information and View. Note that the Owner (i.e. the Creator) still has some permissions.
  • Do the same for the state pending
  • Go back to portal_workflow and set the new workflow talks_workflow for talks. Click Change and then Update security settings.

Note

The addon plone.app.workflowmanager provides a much nicer user-interface for this. The problem is you need a big screen for it and it can be pretty confusing as well.

Done.

Move the changes to the file-system

We don't want to do these steps for every new conference by hand so we move the changes into our package.

Workflow

  • export the GenericSetup step Workflow Tool in http://localhost:8080/Plone/portal_setup/manage_exportSteps.
  • drop the file workflows.xml into profiles/default.
  • drop workflows/talks_workflow/definition.xml in profiles/default/workflows/talks_workflow/definition.xml. The others are just definitions of the default-workflows and we only want things in our package that changes Plone.

Self-registration

This has to happen in python in a custom setuphandler.py since there is not yet a exportable setting for this.

Register a import-step in configure.zcml. It will be automatically run when (re-)installing the addon.

<genericsetup:importStep
  name="ploneconf.site"
  title="ploneconf.site special import handlers"
  description=""
  handler="ploneconf.site.setuphandlers.setupVarious">
    <depends name="typeinfo"/>
</genericsetup:importStep>

Note that the setuphandler has a dependency on typeinfo because it will only allow the creation of talks. For this the type already has to exist.

Create a new file setuphandlers.py

# -*- coding: UTF-8 -*-
from plone.app.controlpanel.security import ISecuritySchema
from plone import api

import logging

PROFILE_ID = 'profile-ploneconf.site:default'
logger = logging.getLogger('ploneconf.site')


def setupVarious(context):

    # Ordinarily, GenericSetup handlers check for the existence of XML files.
    # Here, we are not parsing an XML file, but we use this text file as a
    # flag to check that we actually meant for this import step to be run.
    # The file is found in profiles/default.

    if context.readDataFile('ploneconf.site_various.txt') is None:
        return

    site = api.portal.get()
    set_up_security(site)


def set_up_security(site):
    secSchema = ISecuritySchema(site)
    secSchema.set_enable_self_reg(True)

Add the marker-file profile/default/ploneconf.site_various.txt used in line 18:

The ploneconf.site_various step is run if this file is present in the profile

Grant local roles

Since this applies only to a certain folder in the site we would normally not write code for it.

But we can easily add a method to the setuphandler that creates the folder and sets up some setting for it.

Here is an example:

# -*- coding: UTF-8 -*-
from plone.app.controlpanel.security import ISecuritySchema
from plone import api
from Products.CMFPlone.interfaces.constrains import ISelectableConstrainTypes
from plone.app.dexterity.behaviors import constrains

import logging

PROFILE_ID = 'profile-ploneconf.site:default'
logger = logging.getLogger('ploneconf.site')


def setupVarious(context):

    # Ordinarily, GenericSetup handlers check for the existence of XML files.
    # Here, we are not parsing an XML file, but we use this text file as a
    # flag to check that we actually meant for this import step to be run.
    # The file is found in profiles/default.

    if context.readDataFile('ploneconf.site_various.txt') is None:
        return

    site = api.portal.get()
    set_up_security(site)
    set_up_content(site)


def set_up_security(site):
    secSchema = ISecuritySchema(site)
    secSchema.set_enable_self_reg(True)


def set_up_content(site):
    """Create and configure some initial content"""
    if 'talks' in site:
        return
    talks = api.content.create(
        container=site,
        type='Folder',
        id='talks',
        title='Talks')
    api.content.transition(talks, 'publish')
    api.group.grant_roles(
        groupname='AuthenticatedUsers',
        roles=['Contributor'],
        obj=talks)
    # Enable constraining
    behavior = ISelectableConstrainTypes(talks)
    behavior.setConstrainTypesMode(constrains.ENABLED)
    behavior.setLocallyAllowedTypes(['talk'])
    behavior.setImmediatelyAddableTypes(['talk'])
    logger.info("Created and configured %s" % talks.absolute_url())