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
+
+