You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by he...@apache.org on 2015/07/02 17:09:19 UTC

[04/23] allura git commit: [#7884] Add "Project Features" functionality

[#7884] Add "Project Features" functionality


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/3bbf0f6d
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/3bbf0f6d
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/3bbf0f6d

Branch: refs/heads/hs/7894
Commit: 3bbf0f6de6dfcbbf33a9c70486b917ebac57ce10
Parents: fbb8da9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jun 11 15:08:04 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jun 29 18:04:51 2015 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py           | 13 ++++++++
 .../templates/admin_widgets/features_field.html | 32 ++++++++++++++++++++
 .../templates/admin_widgets/metadata_admin.html |  7 ++++-
 .../ext/admin/templates/project_overview.html   |  2 +-
 Allura/allura/ext/admin/widgets.py              | 18 +++++++++++
 Allura/allura/lib/helpers.py                    | 12 ++++++++
 Allura/allura/model/project.py                  |  1 +
 Allura/allura/tests/functional/test_admin.py    | 16 ++++++++++
 8 files changed, 99 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 46cdf9c..5bde57c 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -222,6 +222,10 @@ class ProjectAdminController(BaseController):
     def overview(self, **kw):
         c.markdown_editor = W.markdown_editor
         c.metadata_admin = W.metadata_admin
+        # need this because features field expects data in specific format
+        metadata_admin_value = h.fixed_attrs_proxy(
+            c.project,
+            features=[{'feature': f} for f in c.project.features])
         c.explain_export_modal = W.explain_export_modal
         show_export_control = asbool(config.get('show_export_control', False))
         allow_project_delete = asbool(config.get('allow_project_delete', True))
@@ -236,6 +240,7 @@ class ProjectAdminController(BaseController):
                 'please contact <a href="mailto:{contact}">{contact}</a>.'.format(contact=config['us_export_contact'])
         return dict(show_export_control=show_export_control,
                     allow_project_delete=allow_project_delete,
+                    metadata_admin_value=metadata_admin_value,
                     explain_export_text=explain_export_text)
 
     @without_trailing_slash
@@ -354,6 +359,7 @@ class ProjectAdminController(BaseController):
     @expose()
     @require_post()
     @validate(W.metadata_admin, error_handler=overview)
+    @h.vardec
     def update(self, name=None,
                short_description=None,
                summary='',
@@ -370,6 +376,7 @@ class ProjectAdminController(BaseController):
                export_controlled=False,
                export_control_type=None,
                tracking_id='',
+               features=None,
                **kw):
         require_access(c.project, 'update')
 
@@ -470,6 +477,12 @@ class ProjectAdminController(BaseController):
             h.log_action(log, 'change project tracking ID').info('')
             M.AuditLog.log('change project tracking ID to %s', tracking_id)
             c.project.tracking_id = tracking_id
+        features = [f['feature'].strip() for f in features or []
+                    if f.get('feature', '').strip()]
+        if features != c.project.features:
+            h.log_action(log, 'change project features').info('')
+            M.AuditLog.log('change project features to %s', features)
+            c.project.features = features
 
         if icon is not None and icon != '':
             if c.project.icon:

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/ext/admin/templates/admin_widgets/features_field.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/admin_widgets/features_field.html b/Allura/allura/ext/admin/templates/admin_widgets/features_field.html
new file mode 100644
index 0000000..c5ef066
--- /dev/null
+++ b/Allura/allura/ext/admin/templates/admin_widgets/features_field.html
@@ -0,0 +1,32 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+
+<div class="{{css_class}}">
+  {% for f in widget.fields %}
+    {% set ctx=widget.context_for(f) %}
+    <div data-name="{{f.name}}">
+      {% if f.show_label %}
+        <div class="grid-4"><label>{{f.label}}:</label></div>
+        <div class="grid-14">{{f.display(**ctx)}}</div>
+      {% else %}
+        <div>{{f.display(**ctx)}}</div>
+      {% endif %}
+    </div>
+  {% endfor %}
+</div>

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html b/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
index 16d1349..a9f9758 100644
--- a/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
+++ b/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
@@ -48,7 +48,12 @@
     {{widget.display_field(widget.fields.short_description) }}
 
     <div style="clear:both">&nbsp;</div>
-    
+
+    <fieldset class="preferences">
+      <legend>{{ widget.display_label(widget.fields.features) }}</legend>
+      {{ widget.display_field(widget.fields.features) }}
+    </fieldset>
+
     {% if tg.config.get('support_tool_choices') %}
     Preferred Support Page (for users of your project):<br>
     {% if c.form_errors.get('support_page_url') %}

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/ext/admin/templates/project_overview.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/project_overview.html b/Allura/allura/ext/admin/templates/project_overview.html
index 3991241..90eca92 100644
--- a/Allura/allura/ext/admin/templates/project_overview.html
+++ b/Allura/allura/ext/admin/templates/project_overview.html
@@ -26,7 +26,7 @@
   {% if c.project.deleted %}
     <div class="notice">This project has been deleted and is not visible to non-admin users</div>
   {% endif %}
-  {{c.metadata_admin.display(value=c.project,
+  {{c.metadata_admin.display(value=metadata_admin_value,
                              show_export_control=show_export_control,
                              allow_project_delete=allow_project_delete)}}
 

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/ext/admin/widgets.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/widgets.py b/Allura/allura/ext/admin/widgets.py
index 5fb5bbd..4ea1fcd 100644
--- a/Allura/allura/ext/admin/widgets.py
+++ b/Allura/allura/ext/admin/widgets.py
@@ -155,6 +155,11 @@ class ScreenshotAdmin(ff.ForgeForm):
         return fields
 
 
+class FeaturesField(ew.CompoundField):
+    template = 'jinja:allura.ext.admin:templates/admin_widgets/features_field.html'
+    fields = [ew.TextField(name='feature', show_label=False)]
+
+
 class MetadataAdmin(ff.AdminForm):
     template = 'jinja:allura.ext.admin:templates/admin_widgets/metadata_admin.html'
     defaults = dict(
@@ -180,6 +185,19 @@ class MetadataAdmin(ff.AdminForm):
                                             fev.UnicodeString(max=1000),
                                             V.MaxBytesValidator(max=1000)),
                                         attrs=dict(title="Add a few paragraphs describing your project to new users."))
+        # Apparently, child field must be CompoundField with custom template
+        # for SortableRepeatedField to work properly, that's why FeaturesField
+        # is not just ew.TextField
+        features = ffw.SortableRepeatedField(
+            label='Features',
+            empty_msg='No features yet',
+            nonempty_msg='Drag and drop features to reorder. '
+                         'Leave empty to delete a feature.',
+            button=ew.InputField(
+                css_class='add',
+                field_type='button',
+                value='Add feature'),
+            field=FeaturesField())
         icon = ew.FileField(label='Icon')
         external_homepage = ew.InputField(field_type="text", label='Homepage',
                                           validator=fev.URL(add_http=True))

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 1c562f0..3a85c19 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -526,6 +526,18 @@ class proxy(object):
         return self._obj(*args, **kwargs)
 
 
+class fixed_attrs_proxy(proxy):
+    """
+    On attribute lookup, if keyword parameter matching attribute name was
+    provided during object construction, returns it's value. Otherwise proxies
+    to obj.
+    """
+    def __init__(self, obj, **kw):
+        self._obj = obj
+        for k, v in kw.iteritems():
+            setattr(self, k, v)
+
+
 def render_genshi_plaintext(template_name, **template_vars):
     assert os.path.exists(template_name)
     fd = open(template_name)

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 2b0566c..b45af77 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -241,6 +241,7 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
     trove_environment = FieldProperty([S.ObjectId])
     tracking_id = FieldProperty(str, if_missing='')
     is_nbhd_project = FieldProperty(bool, if_missing=False)
+    features = FieldProperty([str])
 
     # transient properties
     notifications_disabled = False

http://git-wip-us.apache.org/repos/asf/allura/blob/3bbf0f6d/Allura/allura/tests/functional/test_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index d239591..bd7f373 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -161,6 +161,22 @@ class TestProjectAdmin(TestController):
         r = self.app.get('/admin/audit/')
         assert "uninstall tool test-tool" in r.body, r.body
 
+    def test_features(self):
+        proj = M.Project.query.get(shortname='test')
+        assert_equals(proj.features, [])
+        with audits(u"change project features to \[u'One', u'Two'\]"):
+            self.app.post('/admin/update', params={
+                'features-0.feature': 'One',
+                'features-1.feature': '  ',
+                'features-2.feature': ' Two '})
+        r = self.app.get('/admin/overview')
+        features = r.html.find('fieldset').findAll('input', {'type': 'text'})
+        assert_equals(len(features), 2+1)  # two features + extra empty input
+        assert_equals(features[0]['value'], u'One')
+        assert_equals(features[1]['value'], u'Two')
+        proj = M.Project.query.get(shortname='test')
+        assert_equals(proj.features, [u'One', u'Two'])
+
     def test_admin_export_control(self):
         self.app.get('/admin/')
         with audits('change project export controlled status to True'):