You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by st...@apache.org on 2013/02/25 21:53:41 UTC
[2/3] organization files added simone
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/controller/organizationtool.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/controller/organizationtool.py b/ForgeOrganization/forgeorganization/tool/controller/organizationtool.py
new file mode 100644
index 0000000..078ed72
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/tool/controller/organizationtool.py
@@ -0,0 +1,144 @@
+from tg import expose, validate, redirect, flash
+from tg.decorators import with_trailing_slash
+from pylons import c
+from allura.lib import validators as V
+from allura.lib.decorators import require_post
+from allura import model as M
+from allura.lib.security import require_authenticated
+from allura.controllers import BaseController
+import forgeorganization.tool.widgets.forms as forms
+from forgeorganization.organization.model import Organization
+from forgeorganization.organization.model import ProjectInvolvement
+import re
+from datetime import datetime
+
+class Forms(object):
+ search_form=forms.SearchOrganizationForm(action='search')
+ invite_form=forms.InviteOrganizationForm(action='invite')
+ collaboration_request_form=forms.SendCollaborationRequestForm(
+ action='send_request')
+ def new_change_status_form(self):
+ return forms.ChangeCollaborationStatusForm(
+ action='update_collaboration_status')
+
+F = Forms()
+
+class OrganizationToolController(BaseController):
+
+ @expose('jinja:forgeorganization:tool/templates/index.html')
+ @with_trailing_slash
+ def index(self, **kw):
+ is_admin=c.user.username in c.project.admins()
+ cooperations=[el for el in c.project.organizations
+ if el.collaborationtype=='cooperation']
+ participations=[el for el in c.project.organizations
+ if el.collaborationtype=='participation']
+ user_organizations=[o.organization for o in c.user.memberships
+ if o.membertype == 'admin']
+ return dict(
+ user_organizations=user_organizations,
+ cooperations=cooperations,
+ participations=participations,
+ is_admin=is_admin,
+ forms = F)
+
+ @expose('jinja:forgeorganization:tool/templates/search_results.html')
+ @require_post()
+ @with_trailing_slash
+ @validate(F.search_form, error_handler=index)
+ def search(self, organization, **kw):
+ regx = re.compile(organization, re.IGNORECASE)
+ orgs = Organization.query.find(dict(fullname=regx))
+ return dict(
+ orglist = orgs,
+ forms = F,
+ search_string = organization)
+
+ @expose()
+ @require_post()
+ @validate(V.NullValidator(), error_handler=index)
+ def invite(self, organizationid, collaborationtype, **kw):
+ require_authenticated()
+ is_admin=c.user.username in c.project.admins()
+ if not is_admin:
+ flash("You are not allowed to perform this action", "error")
+ redirect(".")
+ return
+ org = Organization.getById(organizationid)
+ if not org:
+ flash("Invalid organization.", "error")
+ redirect(".")
+ return
+ result = ProjectInvolvement.insert(
+ status='invitation',
+ collaborationtype=collaborationtype,
+ organization_id=organizationid,
+ project_id=c.project._id)
+ if not result:
+ flash("This organization is already involved in this project.",
+ "error")
+ else:
+ flash("Invitation correctly sent.")
+ redirect(".")
+
+ @expose()
+ @require_post()
+ @validate(V.NullValidator(), error_handler=index)
+ def update_collaboration_status(self, collaborationid, collaborationtype, status, **kw):
+ require_authenticated()
+ is_admin=c.user.username in c.project.admins()
+ if not is_admin:
+ flash("You are not allowed to perform this action", "error")
+ redirect(".")
+ return
+
+ allowed = True
+ coll = ProjectInvolvement.getById(collaborationid)
+ if not coll:
+ allowed = False
+ if coll.status != status:
+ if coll.status=='invitation' and status!='remove':
+ allowed=False
+ elif coll.status=='closed':
+ allowed=False
+ elif coll.status=='active' and status!='closed':
+ allowed=False
+ elif coll.status=='request' and status not in ['active','remove']:
+ allowed=False
+
+ if allowed:
+ if status=='closed':
+ collaborationtype=coll.collaborationtype
+ if status=='remove':
+ ProjectInvolvement.delete(coll._id)
+ else:
+ coll.collaborationtype=collaborationtype
+ coll.setStatus(status)
+ flash('The information about this collaboration has been updated')
+ else:
+ flash("You are not allowed to perform this action", "error")
+ redirect('.')
+
+ @expose()
+ @require_post()
+ @validate(V.NullValidator(), error_handler=index)
+ def send_request(self, organization, coll_type, **kw):
+ organization = Organization.getById(organization)
+
+ if not organization or organization.userPermissions(c.user)!='admin':
+ flash("You are not allowed to perform this action.", "error")
+ redirect(".")
+ return
+
+ for org in c.project.organizations:
+ if org.organization == organization and org.status!='closed':
+ flash(
+ "This organization is already included in this project.",
+ "error")
+ redirect(".")
+ return
+ ProjectInvolvement.insert('request', coll_type, organization._id,
+ c.project._id)
+ flash("Your collaboration request was successfully sent.")
+ redirect(".")
+ return
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/main.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/main.py b/ForgeOrganization/forgeorganization/tool/main.py
new file mode 100644
index 0000000..af2e14e
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/tool/main.py
@@ -0,0 +1,91 @@
+#-*- python -*-
+import logging
+from pylons import c
+import formencode
+from formencode import validators
+from webob import exc
+
+from allura.app import Application, SitemapEntry
+from allura.lib import helpers as h
+from allura.lib.security import has_access
+from allura import model as M
+
+from forgeorganization import version
+from forgeorganization.tool.controller import OrganizationToolController
+
+from ming.orm import session
+
+log = logging.getLogger(__name__)
+
+class ForgeOrganizationToolApp(Application):
+ __version__ = version.__version__
+ tool_label='Organizations'
+ default_mount_label='Organizations'
+ default_mount_point='organizations'
+ permissions = ['configure', 'read', 'write',
+ 'unmoderated_post', 'post', 'moderate', 'admin']
+ ordinal=15
+ installable=True
+ config_options = Application.config_options
+ default_external_feeds = []
+ icons={
+ 24:'images/org_24.png',
+ 32:'images/org_32.png',
+ 48:'images/org_48.png'
+ }
+ root = OrganizationToolController()
+
+ def __init__(self, project, config):
+ Application.__init__(self, project, config)
+ role_admin = M.ProjectRole.by_name('Admin')._id
+ role_anon = M.ProjectRole.by_name('*anonymous')._id
+ self.config.acl = [
+ M.ACE.allow(role_anon, 'read'),
+ M.ACE.allow(role_admin, 'admin')]
+
+ def main_menu(self):
+ return [SitemapEntry(self.config.options.mount_label.title(), '.')]
+
+ @property
+ @h.exceptionless([], log)
+ def sitemap(self):
+ menu_id = self.config.options.mount_label.title()
+ with h.push_config(c, app=self):
+ return [
+ SitemapEntry(menu_id, '.')[self.sidebar_menu()] ]
+
+ @property
+ def show_discussion(self):
+ if 'show_discussion' in self.config.options:
+ return self.config.options['show_discussion']
+ else:
+ return True
+
+ @h.exceptionless([], log)
+ def sidebar_menu(self):
+ base = c.app.url
+ links = [SitemapEntry('Home', base)]
+ return links
+
+ def admin_menu(self):
+ admin_url=c.project.url()+'admin/'+self.config.options.mount_point+'/'
+ links = [SitemapEntry(
+ 'Involved organizations',
+ admin_url + 'edit_label',
+ className='admin_modal')]
+ return links
+
+ def install(self, project):
+ #It doesn't make any sense to install the tool twice on the same
+ #project therefore, if it already exists, it doesn't install it
+ #a second time.
+ for tool in project.app_configs:
+ if tool.tool_name == 'organizationstool':
+ if self.config.options.mount_point!=tool.options.mount_point:
+ project.uninstall_app(self.config.options.mount_point)
+ return
+
+ def uninstall(self, project):
+ self.config.delete()
+ session(self.config).flush()
+
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/templates/index.html
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/templates/index.html b/ForgeOrganization/forgeorganization/tool/templates/index.html
new file mode 100644
index 0000000..8092a39
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/tool/templates/index.html
@@ -0,0 +1,114 @@
+{% extends g.theme.master %}
+
+{% block title %}Organizations involved in {{c.project.name}}{% endblock %}
+
+{% block header %}Organizations involved in {{c.project.name}}{% endblock %}
+
+{% block content %}
+
+ <div class="grid-20">
+ <h2>Involved organizations</h2>
+ </div>
+
+ <div class="grid-20">
+ <h3>Organizations cooperating at the project as main partners</h3>
+ {% if cooperations %}
+ {% if is_admin %}
+ <table>
+ <thead>
+ <tr>
+ <th>Organization</th>
+ <th>Type</th>
+ <th>Status</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for oc in cooperations %}
+ <tr>{{forms.new_change_status_form().display(collaboration=oc)}}</tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <table>
+ <thead>
+ <tr>
+ <th>Organization</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for oc in cooperations %}
+ <tr>
+ <td><a href="{{oc.organization.url()}}">{{oc.organization.fullname}}</a></td>
+ <td>{{oc.status.capitalize()}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ {% else %}
+ <p>There are no organizations involved as main cooperators in this project.</p>
+ {% endif %}
+ </div>
+
+ <div class="grid-20">
+ <h3>Other organizations participating at the project</h3>
+ {% if participations %}
+ {% if is_admin %}
+ <table>
+ <thead>
+ <tr>
+ <th>Organization</th>
+ <th>Type</th>
+ <th>Status</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for oc in participations %}
+ <tr>{{forms.new_change_status_form().display(collaboration=oc)}}</tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <table>
+ <thead>
+ <tr>
+ <th>Organization</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for oc in participations %}
+ <tr>
+ <td><a href="{{oc.organization.url()}}">{{oc.organization.fullname}}</a></td>
+ <td>{{oc.status.capitalize()}}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ {% else %}
+ <p>There are no organizations involved as participants in this project.</p>
+ {% endif %}
+ </div>
+
+ {% if is_admin %}
+ <div class="grid-20">
+ <h2>Invite an organization</h2>
+ </div>
+ <div class="grid-20">
+ {{forms.search_form.display()}}
+ </div>
+ {% else %}
+ {% if user_organizations %}
+ <div class="grid-20">
+ <h2>Send collaboration request</h2>
+ </div>
+ <div class="grid-20">
+ {{forms.collaboration_request_form.display(organizations=user_organizations)}}
+ </div>
+ {% endif %}
+ {% endif %}
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/templates/search_results.html
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/templates/search_results.html b/ForgeOrganization/forgeorganization/tool/templates/search_results.html
new file mode 100644
index 0000000..4b4eee2
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/tool/templates/search_results.html
@@ -0,0 +1,37 @@
+{% extends g.theme.master %}
+
+{% block title %}Invite an organization to collaborate to {{c.project.name}}{% endblock %}
+
+{% block header %}Invite an organization to collaborate to {{c.project.name}}{% endblock %}
+
+{% block content %}
+
+ <div class="grid-20">
+ <h2>Search results for {{search_string}}</h2>
+ </div>
+
+ <div class="grid-20">
+ {%if orglist%}
+ <table>
+ <thead>
+ <th>Organization</th>
+ <th>Collaboration type</th>
+ <th>Action</th>
+ </thead>
+ <tbody>
+ {%for org in orglist%}
+ {{forms.invite_form.display(organization=org)}}
+ {%endfor%}
+ </tbody>
+ </table>
+ {%else%}
+ <p>No result was found matching the string "{{search_string}}".</p>
+ {%endif%}
+ </div>
+
+ <div class="grid-20">
+ <h2>Search again</h2>
+ {{forms.search_form.display()}}
+ </div>
+
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/widgets/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/widgets/__init__.py b/ForgeOrganization/forgeorganization/tool/widgets/__init__.py
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/tool/widgets/forms.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/tool/widgets/forms.py b/ForgeOrganization/forgeorganization/tool/widgets/forms.py
new file mode 100644
index 0000000..6d50fea
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/tool/widgets/forms.py
@@ -0,0 +1,152 @@
+from allura.lib import validators as V
+from allura.lib.widgets.forms import ForgeForm
+
+from formencode import validators as fev
+
+import ew as ew_core
+import ew.jinja2_ew as ew
+
+class SearchOrganizationForm(ForgeForm):
+ defaults=dict(ForgeForm.defaults, submit_text='Search')
+
+ class fields(ew_core.NameList):
+ organization=ew.TextField(
+ label='Organization',
+ validator=fev.UnicodeString(not_empty=True))
+
+class InviteOrganizationForm(ForgeForm):
+ defaults=dict(ForgeForm.defaults, submit_text=None, show_errors=False)
+
+ def display(self, **kw):
+ org = kw.get('organization')
+ orgnamefield = '<a href="%s">%s</a>' % (org.url(), org.fullname)
+
+ self.fields = [
+ ew.RowField(
+ show_errors=False,
+ hidden_fields=[
+ ew.HiddenField(
+ name="organizationid",
+ attrs={'value':str(org._id)},
+ show_errors=False)
+ ],
+ fields=[
+ ew.HTMLField(
+ text=orgnamefield,
+ show_errors=False),
+ ew.SingleSelectField(
+ name='collaborationtype',
+ options = [
+ ew.Option(
+ py_value='cooperation',
+ label='Cooperation'),
+ ew.Option(
+ py_value='participation',
+ label='Participation')],
+ validator=fev.UnicodeString(not_empty=True)),
+ ew.SubmitButton(
+ show_label=False,
+ attrs={'value':'Invite'},
+ show_errors=False)])]
+ return super(InviteOrganizationForm, self).display(**kw)
+
+class SendCollaborationRequestForm(ForgeForm):
+ defaults=dict(ForgeForm.defaults, submit_text='Send')
+
+ class fields(ew_core.NameList):
+ organization = ew.SingleSelectField(
+ label='Organization',
+ options = [],
+ validator=fev.UnicodeString(not_empty=True))
+
+ coll_type = ew.SingleSelectField(
+ label='Collaboration Type',
+ options = [
+ ew.Option(
+ py_value='cooperation',
+ label='Cooperation'),
+ ew.Option(
+ py_value='participation',
+ label='Participation')],
+ validator=fev.UnicodeString(not_empty=True))
+
+ def display(self, **kw):
+ orgs = kw.get('organizations')
+ opts=[ew.Option(py_value=org._id,label=org.fullname) for org in orgs]
+ self.fields['organization'].options = opts
+ return super(SendCollaborationRequestForm, self).display(**kw)
+
+class ChangeCollaborationStatusForm(ForgeForm):
+ defaults=dict(ForgeForm.defaults, submit_text=None, show_errors=False)
+
+ def display(self, **kw):
+ coll = kw.get('collaboration')
+ org = coll.organization
+ orgnamefield = '<a href="%s">%s</a>' % (org.url(), org.fullname)
+
+ select_cooperation = (coll.collaborationtype=='cooperation')
+ if coll.status=='closed':
+ options=[ew.Option(py_value='closed',label='Closed',selected=True)]
+ elif coll.status=='active':
+ options=[
+ ew.Option(py_value='closed',label='Closed',selected=False),
+ ew.Option(py_value='active',label='Active',selected=True)]
+ elif coll.status=='invitation':
+ options=[
+ ew.Option(
+ py_value='invitation',
+ label='Pending invitation',
+ selected=True),
+ ew.Option(
+ py_value='remove',
+ label='Remove invitation',
+ selected=False)]
+ elif coll.status=='request':
+ options=[
+ ew.Option(
+ py_value='request',
+ label='Pending request',
+ selected=True),
+ ew.Option(
+ py_value='remove',
+ label='Decline request',
+ selected=False),
+ ew.Option(
+ py_value='active',
+ label='Accept request',
+ selected=False)]
+ self.fields = [
+ ew.RowField(
+ show_errors=False,
+ hidden_fields=[
+ ew.HiddenField(
+ name="collaborationid",
+ attrs={'value':str(coll._id)},
+ show_errors=False)
+ ],
+ fields=[
+ ew.HTMLField(
+ text=orgnamefield,
+ show_errors=False),
+ ew.SingleSelectField(
+ name='collaborationtype',
+ options = [
+ ew.Option(
+ py_value='cooperation',
+ selected=select_cooperation,
+ label='Cooperation'),
+ ew.Option(
+ py_value='participation',
+ selected=not select_cooperation,
+ label='Participation')],
+ validator=fev.UnicodeString(not_empty=True)),
+ ew.SingleSelectField(
+ name='status',
+ options = options,
+ validator=fev.UnicodeString(not_empty=True)),
+ ew.SubmitButton(
+ show_label=False,
+ attrs={'value':'Save'},
+ show_errors=False)])]
+ return super(ChangeCollaborationStatusForm, self).display(**kw)
+
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/forgeorganization/version.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/forgeorganization/version.py b/ForgeOrganization/forgeorganization/version.py
new file mode 100644
index 0000000..6514373
--- /dev/null
+++ b/ForgeOrganization/forgeorganization/version.py
@@ -0,0 +1,2 @@
+__version_info__ = (0, 0)
+__version__ = '.'.join(map(str, __version_info__))
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/setup.py
----------------------------------------------------------------------
diff --git a/ForgeOrganization/setup.py b/ForgeOrganization/setup.py
new file mode 100644
index 0000000..5330aa1
--- /dev/null
+++ b/ForgeOrganization/setup.py
@@ -0,0 +1,33 @@
+from setuptools import setup, find_packages
+import sys, os
+
+from forgeorganization.version import __version__
+
+setup(name='ForgeOrganization',
+ version=__version__,
+ description="",
+ long_description="""\
+""",
+ classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+ keywords='',
+ author='',
+ author_email='',
+ url='',
+ license='',
+ packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ # -*- Extra requirements: -*-
+ 'allura',
+ ],
+ entry_points="""
+ # -*- Entry points: -*-
+ [allura.organization]
+ organization=forgeorganization.organization.main:ForgeOrganizationApp
+
+ [allura]
+ organizationstool=forgeorganization.tool.main:ForgeOrganizationToolApp
+
+ """,
+ )
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/f7cc73d4/ForgeOrganization/test.ini
----------------------------------------------------------------------
diff --git a/ForgeOrganization/test.ini b/ForgeOrganization/test.ini
new file mode 100644
index 0000000..dd41628
--- /dev/null
+++ b/ForgeOrganization/test.ini
@@ -0,0 +1,57 @@
+#
+# allura - TurboGears 2 testing environment configuration
+#
+# The %(here)s variable will be replaced with the parent directory of this file
+#
+[DEFAULT]
+debug = true
+organizations.enable = true
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 5000
+
+[app:main]
+use = config:../Allura/test.ini
+
+[app:main_without_authn]
+use = config:../Allura/test.ini#main_without_authn
+
+[app:main_with_amqp]
+use = config:../Allura/test.ini#main_with_amqp
+
+[loggers]
+keys = root, allura, tool
+
+[handlers]
+keys = test
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = test
+
+[logger_allura]
+level = DEBUG
+handlers =
+qualname = allura
+
+[logger_tool]
+level = DEBUG
+handlers =
+qualname = forgeorganization
+
+[handler_test]
+class = FileHandler
+args = ('test.log',)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
+
+