You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/07/22 16:41:23 UTC

[01/29] allura git commit: [#5943] ticket:815 Option to run setup-app w/o creating test data

Repository: allura
Updated Branches:
  refs/heads/ib/7685 c1a103d29 -> dd55af37a (forced update)


[#5943] ticket:815 Option to run setup-app w/o creating test data


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

Branch: refs/heads/ib/7685
Commit: b67726431b88dc1baaaaf3542f6ba5bdf0581e94
Parents: 48d7c56
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 9 15:02:33 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:36 2015 +0000

----------------------------------------------------------------------
 Allura/allura/websetup/bootstrap.py | 168 ++++++++++++++++++-------------
 1 file changed, 98 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/b6772643/Allura/allura/websetup/bootstrap.py
----------------------------------------------------------------------
diff --git a/Allura/allura/websetup/bootstrap.py b/Allura/allura/websetup/bootstrap.py
index ed76c15..37e250e 100644
--- a/Allura/allura/websetup/bootstrap.py
+++ b/Allura/allura/websetup/bootstrap.py
@@ -75,6 +75,8 @@ def bootstrap(command, conf, vars):
         REGISTRY.register(ew.widget_context,
                           ew.core.WidgetContext('http', ew.ResourceManager()))
 
+    create_test_data = asbool(os.getenv('ALLURA_TEST_DATA', True))
+
     # if this is a test_run, skip user project creation to save time
     make_user_projects = not test_run
 
@@ -101,7 +103,7 @@ def bootstrap(command, conf, vars):
     index = EnsureIndexCommand('ensure_index')
     index.run([''])
 
-    if asbool(conf.get('cache_test_data')):
+    if create_test_data and asbool(conf.get('cache_test_data')):
         if restore_test_data():
             h.set_context('test', neighborhood='Projects')
             return
@@ -114,7 +116,25 @@ def bootstrap(command, conf, vars):
         display_name='Anonymous')
 
     # never make a user project for the root user
-    root = create_user('Root', make_project=False)
+    if create_test_data:
+        root = create_user('Root', make_project=False)
+    else:
+        # TODO: ask user to provide username/password for root
+        root_name = raw_input('Enter username for root user (default "root"): ').strip()
+        if not root_name:
+            root_name = 'root'
+        ok = False
+        while not ok:
+            root_password1 = raw_input('Enter password: ').strip()
+            root_password2 = raw_input('Confirm password: ').strip()
+            if len(root_password1) == 0:
+                log.info('Please, provide password')
+                return
+            if root_password1 != root_password2:
+                log.info("Passwords don't match")
+                return
+            root = create_user(root_name, password=root_password1, make_project=False)
+            ok = True
 
     n_projects = M.Neighborhood(name='Projects', url_prefix='/p/',
                                 features=dict(private_projects=True,
@@ -128,18 +148,12 @@ def bootstrap(command, conf, vars):
                                            max_projects=None,
                                            css='none',
                                            google_analytics=False))
-    n_adobe = M.Neighborhood(
-        name='Adobe', url_prefix='/adobe/', project_list_url='/adobe/',
-        features=dict(private_projects=True,
-                      max_projects=None,
-                      css='custom',
-                      google_analytics=True))
+
     assert tg.config['auth.method'] == 'local'
     project_reg = plugin.ProjectRegistrationProvider.get()
     p_projects = project_reg.register_neighborhood_project(
         n_projects, [root], allow_register=True)
     p_users = project_reg.register_neighborhood_project(n_users, [root])
-    p_adobe = project_reg.register_neighborhood_project(n_adobe, [root])
 
     def set_nbhd_wiki_content(nbhd_proj, content):
         wiki = nbhd_proj.app_instance('wiki')
@@ -164,30 +178,37 @@ def bootstrap(command, conf, vars):
 
         [[projects show_total=yes]]
         '''))
-    set_nbhd_wiki_content(p_adobe, dedent('''
-        This is the "Adobe" neighborhood.  It is just an example of having projects in a different neighborhood.
-
-        [Neighborhood administration](/adobe/admin)
-
-        [[projects show_total=yes]]
-        '''))
+    if create_test_data:
+        n_adobe = M.Neighborhood(
+            name='Adobe', url_prefix='/adobe/', project_list_url='/adobe/',
+            features=dict(private_projects=True,
+                          max_projects=None,
+                          css='custom',
+                          google_analytics=True))
+        p_adobe = project_reg.register_neighborhood_project(n_adobe, [root])
+        set_nbhd_wiki_content(p_adobe, dedent('''
+            This is the "Adobe" neighborhood.  It is just an example of having projects in a different neighborhood.
+
+            [Neighborhood administration](/adobe/admin)
+
+            [[projects show_total=yes]]
+            '''))
+        # add the adobe icon
+        file_name = 'adobe_icon.png'
+        file_path = os.path.join(
+            allura.__path__[0], 'public', 'nf', 'images', file_name)
+        M.NeighborhoodFile.from_path(file_path, neighborhood_id=n_adobe._id)
 
     ThreadLocalORMSession.flush_all()
     ThreadLocalORMSession.close_all()
 
-    # add the adobe icon
-    file_name = 'adobe_icon.png'
-    file_path = os.path.join(
-        allura.__path__[0], 'public', 'nf', 'images', file_name)
-    M.NeighborhoodFile.from_path(file_path, neighborhood_id=n_adobe._id)
-
-    # Add some test users
-    for unum in range(10):
-        make_user('Test User %d' % unum)
+    if create_test_data:
+        # Add some test users
+        for unum in range(10):
+            make_user('Test User %d' % unum)
 
     log.info('Creating basic project categories')
     cat1 = M.ProjectCategory(name='clustering', label='Clustering')
-
     cat2 = M.ProjectCategory(name='communications', label='Communications')
     cat2_1 = M.ProjectCategory(
         name='synchronization', label='Synchronization', parent_id=cat2._id)
@@ -202,36 +223,40 @@ def bootstrap(command, conf, vars):
     cat3_2 = M.ProjectCategory(
         name='engines_servers', label='Engines/Servers', parent_id=cat3._id)
 
-    log.info('Registering "regular users" (non-root) and default projects')
-    # since this runs a lot for tests, separate test and default users and
-    # do the minimal needed
-    if asbool(conf.get('load_test_data')):
-        u_admin = make_user('Test Admin')
-        u_admin.preferences = dict(email_address='test-admin@users.localhost')
-        u_admin.email_addresses = ['test-admin@users.localhost']
-        u_admin.set_password('foo')
-        u_admin.claim_address('test-admin@users.localhost')
-        ThreadLocalORMSession.flush_all()
-
-        admin_email = M.EmailAddress.get(email='test-admin@users.localhost')
-        admin_email.confirmed = True
-    else:
-        u_admin = make_user('Admin 1', username='admin1')
-        # Admin1 is almost root, with admin access for Users and Projects
-        # neighborhoods
-        p_projects.add_user(u_admin, ['Admin'])
-        p_users.add_user(u_admin, ['Admin'])
-
-        p_allura = n_projects.register_project('allura', u_admin, 'Allura')
-    u1 = make_user('Test User')
-    p_adobe1 = n_adobe.register_project('adobe-1', u_admin, 'Adobe project 1')
-    p_adobe.add_user(u_admin, ['Admin'])
-    p0 = n_projects.register_project('test', u_admin, 'Test Project')
-    p1 = n_projects.register_project('test2', u_admin, 'Test 2')
-    p0._extra_tool_status = ['alpha', 'beta']
+    if create_test_data:
+        log.info('Registering "regular users" (non-root) and default projects')
+        # since this runs a lot for tests, separate test and default users and
+        # do the minimal needed
+        if asbool(conf.get('load_test_data')):
+            u_admin = make_user('Test Admin')
+            u_admin.preferences = dict(email_address='test-admin@users.localhost')
+            u_admin.email_addresses = ['test-admin@users.localhost']
+            u_admin.set_password('foo')
+            u_admin.claim_address('test-admin@users.localhost')
+            ThreadLocalORMSession.flush_all()
+
+            admin_email = M.EmailAddress.get(email='test-admin@users.localhost')
+            admin_email.confirmed = True
+        else:
+            u_admin = make_user('Admin 1', username='admin1')
+            # Admin1 is almost root, with admin access for Users and Projects
+            # neighborhoods
+            p_projects.add_user(u_admin, ['Admin'])
+            p_users.add_user(u_admin, ['Admin'])
+
+            p_allura = n_projects.register_project('allura', u_admin, 'Allura')
+        u1 = make_user('Test User')
+        p_adobe1 = n_adobe.register_project('adobe-1', u_admin, 'Adobe project 1')
+        p_adobe.add_user(u_admin, ['Admin'])
+        p0 = n_projects.register_project('test', u_admin, 'Test Project')
+        p1 = n_projects.register_project('test2', u_admin, 'Test 2')
+        p0._extra_tool_status = ['alpha', 'beta']
 
     sess = session(M.Neighborhood)  # all the sessions are the same
-    for x in (n_adobe, n_projects, n_users, p_projects, p_users, p_adobe):
+    _list = (n_projects, n_users, p_projects, p_users)
+    if create_test_data:
+        _list += (n_adobe, p_adobe)
+    for x in _list:
         # Ming doesn't detect substructural changes in newly created objects
         # (vs loaded from DB)
         state(x).status = 'dirty'
@@ -249,24 +274,27 @@ def bootstrap(command, conf, vars):
         create_trove_categories = CreateTroveCategoriesCommand('create_trove_categories')
         create_trove_categories.run([''])
 
-        p0.add_user(u_admin, ['Admin'])
-        log.info('Registering initial apps')
-        with h.push_config(c, user=u_admin):
-            p0.install_apps([{'ep_name': ep_name}
-                for ep_name, app in g.entry_points['tool'].iteritems()
-                if app._installable(tool_name=ep_name,
-                                    nbhd=n_projects,
-                                    project_tools=[])
-            ])
-
-    # reload our p0 project so that p0.app_configs is accurate with all the
-    # newly installed apps
+        if create_test_data:
+            p0.add_user(u_admin, ['Admin'])
+            log.info('Registering initial apps')
+            with h.push_config(c, user=u_admin):
+                p0.install_apps([{'ep_name': ep_name}
+                    for ep_name, app in g.entry_points['tool'].iteritems()
+                    if app._installable(tool_name=ep_name,
+                                        nbhd=n_projects,
+                                        project_tools=[])
+                ])
+
     ThreadLocalORMSession.flush_all()
     ThreadLocalORMSession.close_all()
-    p0 = M.Project.query.get(_id=p0._id)
-    sub = p0.new_subproject('sub1', project_name='A Subproject')
-    with h.push_config(c, user=u_admin):
-        sub.install_app('wiki')
+
+    if create_test_data:
+        # reload our p0 project so that p0.app_configs is accurate with all the
+        # newly installed apps
+        p0 = M.Project.query.get(_id=p0._id)
+        sub = p0.new_subproject('sub1', project_name='A Subproject')
+        with h.push_config(c, user=u_admin):
+            sub.install_app('wiki')
 
     ThreadLocalORMSession.flush_all()
     ThreadLocalORMSession.close_all()


[10/29] allura git commit: [#7540] ticket:816 Show attachments in tickets notifications

Posted by je...@apache.org.
[#7540] ticket:816 Show attachments in tickets notifications


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

Branch: refs/heads/ib/7685
Commit: 9d7b26eda21824d356ecd31b017c84671c14f3aa
Parents: f245736
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jul 8 13:11:23 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 20:01:44 2015 +0000

----------------------------------------------------------------------
 Allura/allura/templates/mail/Ticket.txt         |  7 +++++++
 ForgeTracker/forgetracker/model/ticket.py       |  5 ++++-
 .../forgetracker/tests/functional/test_root.py  | 20 ++++++++++++++++++++
 3 files changed, 31 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/9d7b26ed/Allura/allura/templates/mail/Ticket.txt
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/mail/Ticket.txt b/Allura/allura/templates/mail/Ticket.txt
index 64b704c..abe38af 100644
--- a/Allura/allura/templates/mail/Ticket.txt
+++ b/Allura/allura/templates/mail/Ticket.txt
@@ -34,5 +34,12 @@
     **Last Updated:** {{data.mod_date.strftime('%a %b %d, %Y %I:%M %p UTC')}}
 {% endif -%}
 **Owner:** {{data.assigned_to_name()}}
+{% if data.attachments -%}
+    **Attachments:**
+
+{% for att in data.attachments -%}
+    - [{{att.filename}}]({{h.absurl(att.url())}}) ({{h.do_filesizeformat(att.length)}}; {{att.content_type}})
+{% endfor -%}
+{% endif %}
 
 {{data.description}}

http://git-wip-us.apache.org/repos/asf/allura/blob/9d7b26ed/ForgeTracker/forgetracker/model/ticket.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 925a067..20996ff 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -981,9 +981,12 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
                 elif k in other_custom_fields:
                     # strings are good enough for any other custom fields
                     self.custom_fields[k] = v
-        self.commit()
         if attachment is not None:
             self.add_multiple_attachments(attachment)
+            # flush the session to make attachments available in the
+            # notification email
+            ThreadLocalORMSession.flush_all()
+        self.commit()
 
     def _move_attach(self, attachments, attach_metadata, app_config):
         for attach in attachments:

http://git-wip-us.apache.org/repos/asf/allura/blob/9d7b26ed/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 122d707..871f0fd 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -1599,6 +1599,26 @@ class TestFunctionalController(TrackerTestController):
         assert_in('test second ticket', str(ticket_rows))
         assert_false('test third ticket' in str(ticket_rows))
 
+    def test_new_ticket_notification_contains_attachments(self):
+        file_name = u'tést_root.py'.encode('utf-8')
+        file_data = file(__file__).read()
+        upload = ('ticket_form.attachment', file_name, file_data)
+        r = self.app.post('/bugs/save_ticket', {
+            'ticket_form.summary': 'new ticket with attachment'
+        }, upload_files=[upload]).follow()
+        assert_in(file_name, r)
+        ThreadLocalORMSession.flush_all()
+        M.MonQTask.run_ready()
+        ThreadLocalORMSession.flush_all()
+        email = M.MonQTask.query.find(
+            dict(task_name='allura.tasks.mail_tasks.sendmail')
+        ).first()
+        expected_text = (
+            u'**Attachments:**\n\n'
+            u'- [tést_root.py]'
+            u'(http://localhost/p/test/bugs/1/attachment/t%C3%A9st_root.py)')
+        assert_in(expected_text, email.kwargs['text'])
+
     def test_ticket_notification_contains_milestones(self):
         params = dict(
             custom_fields=[


[24/29] allura git commit: [#7685] ticket:819 Use ajax requests in subscription icons

Posted by je...@apache.org.
[#7685] ticket:819 Use ajax requests in subscription icons


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

Branch: refs/heads/ib/7685
Commit: 100d91da39fc02a5c65326a37e92b1be1c00d2ee
Parents: 0ec05d5
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jul 10 18:03:02 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py         |  7 +-
 .../lib/widgets/resources/js/subscriptions.js   | 99 ++++++++++++++++++++
 Allura/allura/lib/widgets/subscriptions.py      |  7 +-
 Allura/allura/templates/widgets/subscribe.html  | 44 ++++-----
 ForgeBlog/forgeblog/main.py                     | 11 ++-
 .../forgediscussion/controllers/forum.py        |  7 +-
 ForgeTracker/forgetracker/tracker_main.py       | 14 ++-
 ForgeWiki/forgewiki/wiki_main.py                |  7 +-
 8 files changed, 158 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index 0178723..2b9ae89 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -688,7 +688,7 @@ class TreeBrowser(BaseController, DispatchIndex):
             self._path + '/' + next,
             self), rest
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
@@ -696,7 +696,10 @@ class TreeBrowser(BaseController, DispatchIndex):
             M.Mailbox.subscribe()
         elif unsubscribe:
             M.Mailbox.unsubscribe()
-        redirect(request.referer)
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(),
+        }
 
 
 class FileBrowser(BaseController):

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/Allura/allura/lib/widgets/resources/js/subscriptions.js
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/resources/js/subscriptions.js b/Allura/allura/lib/widgets/resources/js/subscriptions.js
new file mode 100644
index 0000000..c03bbeb
--- /dev/null
+++ b/Allura/allura/lib/widgets/resources/js/subscriptions.js
@@ -0,0 +1,99 @@
+/*
+       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.
+*/
+
+var dom = React.createElement;
+
+/* top-level form state */
+var state = {
+  'thing': 'tool',
+  'subscribed': false,
+  'url': '',
+  'icon': {}
+};
+
+SubscriptionForm = React.createClass({
+
+  render: function() {
+    var action = this.props.subscribed ? "Unsubscribe from" : "Subscribe to";
+    var title = action + ' this ' + this.props.thing;
+    var link_opts = {
+      className: this.props.subscribed ? 'active' : '',
+      href: '#',
+      title: title,
+      onClick: this.handleClick
+    };
+    var icon_opts = {
+      'data-icon': this.props.icon.char,
+      className: 'ico ' + this.props.icon.css,
+      title: title
+    };
+    return dom('a', link_opts, dom('b', icon_opts));
+  },
+
+  handleClick: function() {
+    var url = this.props.url;
+    var csrf = $.cookie('_session_id');
+    var data = {_session_id: csrf};
+    if (this.props.subscribed) {
+      data.unsubscribe = true;
+    } else {
+      data.subscribe = true;
+    }
+    set_state({in_progress: true});
+    /*
+     * TODO:
+     * - show 'in-progress' status to user somehow
+     * - handle errors (show to the user in some kind of popup/flash?)
+     * - If user is subscribed to the whole tool and she tries to subsribe to
+     *   the artifact she will not be subscribed, so nothing will change in the
+     *   UI and it's confusing. We need to show some information message in
+     *   such case
+     */
+    $.post(url, data, function(resp) {
+      if (resp.status == 'ok') {
+        set_state({subscribed: resp.subscribed});
+      }
+    }).always(function() {
+      set_state({in_progress: false});
+    });
+    return false;
+  }
+
+});
+
+function set_state(new_state) {
+  /* Set state and re-render entire UI */
+  for (var key in new_state) {
+    state[key] = new_state[key];
+  }
+  render(state);
+}
+
+function render(state) {
+  var props = {};
+  for (var key in state) { props[key] = state[key]; }
+  React.render(
+    dom(SubscriptionForm, props),
+    document.getElementById('subscription-form')
+  );
+}
+
+$(function() {
+  set_state(document.SUBSCRIPTION_OPTIONS);
+});

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/Allura/allura/lib/widgets/subscriptions.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/subscriptions.py b/Allura/allura/lib/widgets/subscriptions.py
index dec1274..c8d3dcd 100644
--- a/Allura/allura/lib/widgets/subscriptions.py
+++ b/Allura/allura/lib/widgets/subscriptions.py
@@ -75,7 +75,7 @@ class SubscribeForm(ew.SimpleForm):
     defaults = dict(
         ew.SimpleForm.defaults,
         thing='tool',
-        style='text',
+        style='icon',
         tool_subscribed=False,
         value=None)
 
@@ -86,3 +86,8 @@ class SubscribeForm(ew.SimpleForm):
 
     def from_python(self, value, state):
         return value
+
+    def resources(self):
+        for r in super(SubscribeForm, self).resources():
+            yield r
+        yield ew.JSLink('js/subscriptions.js')

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/Allura/allura/templates/widgets/subscribe.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/subscribe.html b/Allura/allura/templates/widgets/subscribe.html
index 5154eb7..1361a9c 100644
--- a/Allura/allura/templates/widgets/subscribe.html
+++ b/Allura/allura/templates/widgets/subscribe.html
@@ -17,28 +17,24 @@
        under the License.
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
-{% if value %}
-  {% if style == 'icon' %}
-    <form action="{{action}}" id="subscribe_form" style="display:inline;" method="POST">
-      {{lib.csrf_token()}}
-      <input type="hidden" name="unsubscribe" value="1">
-      <a href="#" class="artifact_unsubscribe active" title="Unsubscribe from this {{thing}}" onclick="$('#subscribe_form').submit(); return false;"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Unsubscribe from this {{thing}}"></b></a>
-    </form>
-  {% else %}
-    You are currently subscribed to this {{thing}}.
-    <br/>
-    {{fields['unsubscribe'].display()}}
-  {% endif %}
-{% else %}
-  {% if style == 'icon' %}
-    <form action="{{action}}" id="subscribe_form" style="display:inline;" method="POST">
-      {{lib.csrf_token()}}
-      <input type="hidden" name="subscribe" value="1">
-      <a href="#" class="artifact_subscribe" title="Subscribe to this {{thing}}" onclick="$('#subscribe_form').submit(); return false;"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Subscribe to this {{thing}}"></b></a>
-    </form>
-  {% else %}
-    You are currently not subscribed to this {{thing}}.
-    <br/>
-    {{fields['subscribe'].display()}}
-  {% endif %}
+
+{% do g.register_forge_js('js/react.min.js', location='head_js') %}
+{% for blob in g.resource_manager.emit('head_js') %}
+{% if 'react.min.js' in blob %}
+  {{ blob }}
 {% endif %}
+{% endfor %}
+
+<div id="subscription-form" style="display: inline;">{# see subscription.js #}</div>
+
+<script>
+  document.SUBSCRIPTION_OPTIONS = {
+    "thing": "{{thing}}",
+    "subscribed": {{"true" if value else "false"}},
+    "url": "{{action}}",
+    "icon": {
+      "char": "{{g.icons['mail'].char}}",
+      "css": "{{g.icons['mail'].css}}"
+    }
+  };
+</script>

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 9007f87..adfc172 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -322,8 +322,10 @@ class PostController(BaseController, FeedController):
         version = kw.pop('version', None)
         post = self._get_version(version)
         base_post = self.post
+        subscribed = M.Mailbox.subscribed(artifact=self.post)
         return dict(post=post, base_post=base_post,
-                    page=page, limit=limit, count=post_count)
+                    page=page, limit=limit, count=post_count,
+                    subscribed=subscribed)
 
     @expose('jinja:forgeblog:templates/blog/edit_post.html')
     @without_trailing_slash
@@ -375,7 +377,7 @@ class PostController(BaseController, FeedController):
         self.post.commit()
         redirect('.')
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
@@ -383,7 +385,10 @@ class PostController(BaseController, FeedController):
             self.post.subscribe(type='direct')
         elif unsubscribe:
             self.post.unsubscribe()
-        redirect(h.really_unicode(request.referer).encode('utf-8'))
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(artifact=self.post),
+        }
 
     def get_feed(self, project, app, user):
         """Return a :class:`allura.controllers.feed.FeedArgs` object describing

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/ForgeDiscussion/forgediscussion/controllers/forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/forum.py b/ForgeDiscussion/forgediscussion/controllers/forum.py
index 94fe6d9..b915823 100644
--- a/ForgeDiscussion/forgediscussion/controllers/forum.py
+++ b/ForgeDiscussion/forgediscussion/controllers/forum.py
@@ -122,7 +122,7 @@ class ForumController(DiscussionController):
     def deleted(self):
         return dict()
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(W.subscribe_form)
     def subscribe_to_forum(self, subscribe=None, unsubscribe=None, shortname=None, **kw):
@@ -130,7 +130,10 @@ class ForumController(DiscussionController):
             self.discussion.subscribe(type='direct')
         elif unsubscribe:
             self.discussion.unsubscribe()
-        redirect(h.really_unicode(request.referer).encode('utf-8'))
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(artifact=self.discussion),
+        }
 
 
 class ForumThreadController(ThreadController):

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index c3a83e6..0049957 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1090,7 +1090,7 @@ class RootController(BaseController, FeedController):
             dates=dates,
         )
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None):
@@ -1098,7 +1098,10 @@ class RootController(BaseController, FeedController):
             M.Mailbox.subscribe(type='direct')
         elif unsubscribe:
             M.Mailbox.unsubscribe()
-        redirect(request.referer)
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(),
+        }
 
 
 class BinController(BaseController):
@@ -1520,7 +1523,7 @@ class TicketController(BaseController, FeedController):
         c.app.globals.invalidate_bin_counts()
         redirect('.')
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None):
@@ -1528,7 +1531,10 @@ class TicketController(BaseController, FeedController):
             self.ticket.subscribe(type='direct')
         elif unsubscribe:
             self.ticket.unsubscribe()
-        redirect(request.referer)
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(artifact=self.ticket),
+        }
 
     @expose('json:')
     @require_post()

http://git-wip-us.apache.org/repos/asf/allura/blob/100d91da/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 229a348..9fca1c2 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -737,7 +737,7 @@ class PageController(BaseController, FeedController):
         self.page.add_multiple_attachments(file_info)
         redirect(request.referer)
 
-    @expose()
+    @expose('json:')
     @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
@@ -747,7 +747,10 @@ class PageController(BaseController, FeedController):
             self.page.subscribe(type='direct')
         elif unsubscribe:
             self.page.unsubscribe()
-        redirect(request.referer)
+        return {
+            'status': 'ok',
+            'subscribed': M.Mailbox.subscribed(artifact=self.page),
+        }
 
 
 class WikiAttachmentController(ac.AttachmentController):


[21/29] allura git commit: [#7685] ticket:802 Fixed tests

Posted by je...@apache.org.
[#7685] ticket:802 Fixed tests


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

Branch: refs/heads/ib/7685
Commit: 8699f40fdd897f6f22b2bcaaa0544f56373e3c51
Parents: c5f8bda
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Sat Jun 27 22:53:54 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:25 2015 +0300

----------------------------------------------------------------------
 .../tests/functional/test_forum.py              |  2 +-
 .../forgetracker/tests/functional/test_root.py  | 22 ++++++++++----------
 .../forgewiki/tests/functional/test_rest.py     |  2 +-
 .../forgewiki/tests/functional/test_root.py     | 16 +++++++-------
 4 files changed, 21 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/8699f40f/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 9c5397f..ae91bf0 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -576,7 +576,7 @@ class TestForum(TestController):
             if field.has_key('name') and 'subscription' not in field['name']:
                 params[field['name']] = field.has_key('value') and field['value'] or ''
         self.app.post(str(subscribe_url), params=params)
-        self.app.get('/discussion/general/subscribe_to_forum?subscribe=True')
+        self.app.post('/discussion/general/subscribe_to_forum', {'subscribe': True})
         f = thread.html.find('div', {'class': 'row reply_post_form'}).find('form')
         rep_url = f.get('action')
         params = dict()

http://git-wip-us.apache.org/repos/asf/allura/blob/8699f40f/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 871f0fd..7a84683 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -840,7 +840,7 @@ class TestFunctionalController(TrackerTestController):
         }, upload_files=[upload]).follow()
         assert file_name in ticket_editor, ticket_editor.showbrowser()
         req = self.app.get('/bugs/1/')
-        file_link = req.html.findAll('form')[1].findAll('a')[6]
+        file_link = req.html.findAll('form')[3].findAll('a')[6]
         assert_equal(file_link.string, file_name)
         self.app.post(str(file_link['href']), {
             'delete': 'True'
@@ -882,7 +882,7 @@ class TestFunctionalController(TrackerTestController):
         ticket_editor = self.app.post('/bugs/1/update_ticket', {
             'summary': 'zzz'
         }, upload_files=[upload]).follow()
-        download = self.app.get(str(ticket_editor.html.findAll('form')[1].findAll('a')[7]['href']))
+        download = self.app.get(str(ticket_editor.html.findAll('form')[2].findAll('a')[7]['href']))
         assert_equal(download.body, file_data)
 
     def test_two_attachments(self):
@@ -1239,7 +1239,7 @@ class TestFunctionalController(TrackerTestController):
         # Test edit ticket form
         self.new_ticket(summary='Test ticket')
         response = self.app.get('/bugs/1/')
-        form = response.forms[1]
+        form = response.forms[2]
         assert_equal(
             form['ticket_form.custom_fields._priority'].value, 'normal')
         assert_equal(form['ticket_form.custom_fields._category'].value, '')
@@ -1247,9 +1247,9 @@ class TestFunctionalController(TrackerTestController):
         form['ticket_form.custom_fields._priority'] = 'urgent'
         form['ticket_form.custom_fields._category'] = 'bugs'
         error_form = form.submit()
-        assert_equal(error_form.forms[1]['ticket_form.custom_fields._priority'].value,
+        assert_equal(error_form.forms[2]['ticket_form.custom_fields._priority'].value,
                      'urgent')
-        assert_equal(error_form.forms[1]['ticket_form.custom_fields._category'].value,
+        assert_equal(error_form.forms[2]['ticket_form.custom_fields._category'].value,
                      'bugs')
 
     def test_new_ticket_validation(self):
@@ -1269,7 +1269,7 @@ class TestFunctionalController(TrackerTestController):
         # set a summary, submit, and check for success
         error_form.forms[1]['ticket_form.summary'] = summary
         success = error_form.forms[1].submit().follow().html
-        assert success.findAll('form')[1].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
+        assert success.findAll('form')[2].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
         assert success.find('input', {'name': 'ticket_form.summary'})['value'] == summary
 
     def test_edit_ticket_validation(self):
@@ -1280,7 +1280,7 @@ class TestFunctionalController(TrackerTestController):
         # check that existing form is valid
         assert response.html.find('input', {'name': 'ticket_form.summary'})['value'] == old_summary
         assert not response.html.find('div', {'class': 'error'})
-        form = response.forms[1]
+        form = response.forms[2]
         # try submitting with no summary set and check for error message
         form['ticket_form.summary'] = ""
         error_form = form.submit()
@@ -1289,11 +1289,11 @@ class TestFunctionalController(TrackerTestController):
         assert error_message.string == 'You must provide a Title'
         assert error_message.findPreviousSibling('input').get('name') == 'ticket_form.summary'
         # set a summary, submit, and check for success
-        error_form.forms[1]['ticket_form.summary'] = new_summary
-        r = error_form.forms[1].submit()
+        error_form.forms[2]['ticket_form.summary'] = new_summary
+        r = error_form.forms[2].submit()
         assert r.status_int == 302, r.showbrowser()
         success = r.follow().html
-        assert success.findAll('form')[1].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
+        assert success.findAll('form')[2].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
         assert success.find('input', {'name': 'ticket_form.summary'})['value'] == new_summary
 
     def test_home(self):
@@ -2720,7 +2720,7 @@ class TestCustomUserField(TrackerTestController):
     def test_change_user_field(self):
         kw = {'custom_fields._code_review': ''}
         r = self.new_ticket(summary='test custom fields', **kw).follow()
-        f = r.forms[1]
+        f = r.forms[2]
         # Populate ProjectUserCombo's select with option we want.
         # This is a workaround for tests,
         # in real enviroment this is populated via ajax.

http://git-wip-us.apache.org/repos/asf/allura/blob/8699f40f/ForgeWiki/forgewiki/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/tests/functional/test_rest.py b/ForgeWiki/forgewiki/tests/functional/test_rest.py
index 6a5de3b..a2b58ae 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_rest.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_rest.py
@@ -42,7 +42,7 @@ class TestWikiApi(TestRestApiBase):
 
     def test_get_page(self):
         r = self.app.get('/p/test/wiki/Home/')
-        discussion_url = r.html.findAll('form')[2]['action'][:-4]
+        discussion_url = r.html.findAll('form')[3]['action'][:-4]
         content = file(__file__).read()
         self.app.post('/wiki/Home/attach',
                       upload_files=[('file_info', 'test_root.py', content)])

http://git-wip-us.apache.org/repos/asf/allura/blob/8699f40f/ForgeWiki/forgewiki/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/tests/functional/test_root.py b/ForgeWiki/forgewiki/tests/functional/test_root.py
index aea9028..dafb74f 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_root.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_root.py
@@ -754,24 +754,24 @@ class TestRootController(TestController):
         # user is not subscribed
         assert not M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get('/p/test/wiki/Home/', extra_environ={'username': str(user.username)})
-        link = r.html.find('a', {'href': '/p/test/wiki/subscribe?subscribe=True'})
-        assert link is not None
+        inp = r.html.find('input', {'type': 'hidden', 'name': 'subscribe'})
+        assert inp is not None
         # subscribe
-        self.app.get('/p/test/wiki/subscribe?subscribe=True',
+        self.app.post('/p/test/wiki/subscribe', {'subscribe': True},
                      extra_environ={'username': str(user.username)}).follow()
         # user is subscribed
         assert M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get('/p/test/wiki/Home/', extra_environ={'username': str(user.username)})
-        link = r.html.find('a', {'href': '/p/test/wiki/subscribe?unsubscribe=True'})
-        assert link is not None
+        inp = r.html.find('input', {'type': 'hidden', 'name': 'unsubscribe'})
+        assert inp is not None
         # unsubscribe
-        self.app.get('/p/test/wiki/subscribe?unsubscribe=True',
+        self.app.post('/p/test/wiki/subscribe', {'unsubscribe': True},
                      extra_environ={'username': str(user.username)}).follow()
         # user is not subscribed
         assert not M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get('/p/test/wiki/Home/', extra_environ={'username': str(user.username)})
-        link = r.html.find('a', {'href': '/p/test/wiki/subscribe?subscribe=True'})
-        assert link is not None
+        inp = r.html.find('input', {'type': 'hidden', 'name': 'subscribe'})
+        assert inp is not None
 
     def test_rate_limit_new_page(self):
         # Set rate limit to unlimit


[12/29] allura git commit: [#7927] ticket:821 Implement CORS middleware

Posted by je...@apache.org.
[#7927] ticket:821 Implement CORS middleware


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

Branch: refs/heads/ib/7685
Commit: 0c71798b2dbe73f68f5912a81606b9ca73f45704
Parents: de47627
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 16 15:54:22 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Jul 16 17:50:54 2015 +0300

----------------------------------------------------------------------
 Allura/allura/config/middleware.py     |  9 ++++-
 Allura/allura/lib/custom_middleware.py | 57 +++++++++++++++++++++++++++++
 Allura/development.ini                 |  9 +++++
 3 files changed, 74 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/allura/config/middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index ba07cc4..922034d 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -25,7 +25,7 @@ import tg
 import tg.error
 import pkg_resources
 from tg import config
-from paste.deploy.converters import asbool
+from paste.deploy.converters import asbool, aslist, asint
 from paste.registry import RegistryManager
 from routes.middleware import RoutesMiddleware
 from pylons.middleware import StatusCodeRedirect
@@ -44,6 +44,7 @@ from allura.lib.custom_middleware import AlluraTimerMiddleware
 from allura.lib.custom_middleware import SSLMiddleware
 from allura.lib.custom_middleware import StaticFilesMiddleware
 from allura.lib.custom_middleware import CSRFMiddleware
+from allura.lib.custom_middleware import CORSMiddleware
 from allura.lib.custom_middleware import LoginRedirectMiddleware
 from allura.lib.custom_middleware import RememberLoginMiddleware
 from allura.lib import patches
@@ -137,6 +138,12 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
     # Clear cookies when the CSRF field isn't posted
     if not app_conf.get('disable_csrf_protection'):
         app = CSRFMiddleware(app, '_session_id')
+    if asbool(config.get('cors.enabled', False)):
+        # Handle CORS requests
+        allowed_methods = aslist(config.get('cors.methods'))
+        allowed_headers = aslist(config.get('cors.headers'))
+        cache_duration = asint(config.get('cors.cache_duration', 0))
+        app = CORSMiddleware(app, allowed_methods, allowed_headers)
     # Setup the allura SOPs
     app = allura_globals_middleware(app)
     # Ensure http and https used per config

http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/allura/lib/custom_middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/custom_middleware.py b/Allura/allura/lib/custom_middleware.py
index 13c3db5..4eef689 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -84,6 +84,63 @@ class StaticFilesMiddleware(object):
         return fileapp.FileApp(file_path, [
             ('Access-Control-Allow-Origin', '*')])
 
+class CORSMiddleware(object):
+    '''Enables Cross-Origin Resource Sharing for REST API'''
+
+    def __init__(self, app, allowed_methods, allowed_headers, cache=None):
+        self.app = app
+        self.allowed_methods = [m.upper() for m in allowed_methods]
+        self.allowed_headers = set(h.lower() for h in allowed_headers)
+        self.cache_preflight = cache or None
+
+    def __call__(self, environ, start_response):
+        is_api_request = environ.get('PATH_INFO', '').startswith('/rest/')
+        valid_cors = 'HTTP_ORIGIN' in environ
+        if not is_api_request or not valid_cors:
+            return self.app(environ, start_response)
+
+        method = environ.get('REQUEST_METHOD')
+        acrm = environ.get('HTTP_ACCESS_CONTROL_REQUEST_METHOD')
+        if method == 'OPTIONS' and acrm:
+            return self.handle_preflight_request(environ, start_response)
+        else:
+            return self.handle_simple_request(environ, start_response)
+
+    def handle_simple_request(self, environ, start_response):
+        def cors_start_response(status, headers, exc_info=None):
+            headers.extend(self.get_response_headers(preflight=False))
+            return start_response(status, headers, exc_info)
+        return self.app(environ, cors_start_response)
+
+    def handle_preflight_request(self, environ, start_response):
+        method = environ.get('HTTP_ACCESS_CONTROL_REQUEST_METHOD')
+        if method not in self.allowed_methods:
+            return self.app(environ, start_response)
+        headers = self.get_access_control_request_headers(environ)
+        if not headers.issubset(self.allowed_headers):
+            return self.app(environ, start_response)
+        r = exc.HTTPOk(headers=self.get_response_headers(preflight=True))
+        return r(environ, start_response)
+
+    def get_response_headers(self, preflight=False):
+        headers = [('Access-Control-Allow-Origin', '*')]
+        if preflight:
+            ac_methods = ', '.join(self.allowed_methods)
+            ac_headers = ', '.join(self.allowed_headers)
+            headers.extend([
+                ('Access-Control-Allow-Methods', ac_methods),
+                ('Access-Control-Allow-Headers', ac_headers),
+            ])
+            if self.cache_preflight:
+                headers.append(
+                    ('Access-Control-Max-Age', self.cache_preflight)
+                )
+        return headers
+
+    def get_access_control_request_headers(self, environ):
+        headers = environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', '')
+        return set(h.strip().lower() for h in headers.split(',') if h.strip())
+
 
 class LoginRedirectMiddleware(object):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index 02d3e9f..934ea67 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -216,6 +216,15 @@ webhook.repo_push.limit = 30
 ; `WebhookSender.triggered_by`) and values are actual limits (default=3), e.g.:
 webhook.repo_push.max_hooks = {"git": 3, "hg": 3, "svn": 3}
 
+;; Allow Cross-Origin Resource Sharing (CORS) requests to the REST API
+; disabled by default, uncomment the following options to enable:
+;cors.enabled = true
+;cors.methods = GET HEAD POST PUT DELETE
+;cors.headers = Authorization Accept Content-Type
+; Allow clients to cache preflight responses for N seconds
+; Set to 0 or remove completely to disable
+;cors.cache_duration = 86400
+
 ; Additional fields for admin project/user search
 ; Note: whitespace after comma is important!
 ;search.project.additional_search_fields = private, url, title


[22/29] allura git commit: [#7685] ticket:802 Fixed test

Posted by je...@apache.org.
[#7685] ticket:802 Fixed test


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

Branch: refs/heads/ib/7685
Commit: 75cf25f323d4da7541634fb7a2bb4fb72a386f3d
Parents: 8699f40
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Mon Jun 29 14:01:55 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:25 2015 +0300

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tests/functional/test_root.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/75cf25f3/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 7a84683..23c77f4 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -840,7 +840,7 @@ class TestFunctionalController(TrackerTestController):
         }, upload_files=[upload]).follow()
         assert file_name in ticket_editor, ticket_editor.showbrowser()
         req = self.app.get('/bugs/1/')
-        file_link = req.html.findAll('form')[3].findAll('a')[6]
+        file_link = req.html.findAll('form')[2].findAll('a')[6]
         assert_equal(file_link.string, file_name)
         self.app.post(str(file_link['href']), {
             'delete': 'True'


[06/29] allura git commit: [#5943] ticket:815 Post-setup docs

Posted by je...@apache.org.
[#5943] ticket:815 Post-setup docs


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

Branch: refs/heads/ib/7685
Commit: ccbd34f67544034592d264bd65a70d15c2942941
Parents: 14ed0bd
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 9 18:06:13 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:37 2015 +0000

----------------------------------------------------------------------
 Allura/docs/getting_started/administration.rst |  1 +
 Allura/docs/getting_started/installation.rst   | 51 ++++++++++++++++++++-
 Allura/docs/getting_started/using.rst          | 41 +++++++++++++++++
 3 files changed, 92 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ccbd34f6/Allura/docs/getting_started/administration.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/administration.rst b/Allura/docs/getting_started/administration.rst
index 2b49c4e..fc56e5a 100644
--- a/Allura/docs/getting_started/administration.rst
+++ b/Allura/docs/getting_started/administration.rst
@@ -22,6 +22,7 @@ Administration
 .. contents::
    :local:
 
+.. _site-admin-interface:
 
 Site Admin Interface
 ====================

http://git-wip-us.apache.org/repos/asf/allura/blob/ccbd34f6/Allura/docs/getting_started/installation.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/installation.rst b/Allura/docs/getting_started/installation.rst
index 9c08692..30425b4 100644
--- a/Allura/docs/getting_started/installation.rst
+++ b/Allura/docs/getting_started/installation.rst
@@ -24,10 +24,59 @@ Our step-by-step setup instructions are in our INSTALL.markdown file.  You can r
 
 For a faster and easier setup, see our `Vagrant/VirtualBox installation guide <https://forge-allura.apache.org/p/allura/wiki/Install%20and%20Run%20Allura%20-%20Vagrant/>`_
 
+That's all for the development setup.  For the production setup see :ref:`next section <post-setup-instructions>`.
+
+.. _post-setup-instructions:
+
+Post-setup instructions
+-----------------------
+
+Create project for site-admin
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+First of all you need to create a project, which will serve as a container for keeping site administrators (users who will have access to the :ref:`admin interface <site-admin-interface>`).
+
+In order to do that:
+
+- open main page of the site in your browser
+- go to "Projects" neighborhood (:ref:`what-are-neighborhoods`)
+- click "Register a new project" link
+
+By default all admins of "allura" project in "Projects" neighborhood are treated as site admins. If you want to use different project for that, change `site_admins_project` in :file:`development.ini`.
+
+Change default configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the :file:`development.ini`:
+
+Change `[handler_console]` section, so that logs go to a file and will include background tasks info.
+
+.. code-block:: ini
+
+    class = allura.lib.utils.CustomWatchedFileHandler
+    args = ('/path/to/allura.log', 'a')
+
+Add write permissions to the :file:`/path/to/allura.log` for the user you use to run allura proccess.
+
+Change "secrets".
+
+.. code-block:: ini
+
+    beaker.session.secret = <your-secret-key>
+    beaker.session.validate_key = <yet-another-secret-key>
+
+The first one is used for simple cookies, the latter is used for encrypted cookies.
+
+You can use the following command to generate a good key:
+
+.. code-block:: bash
+
+    ~$ python -c 'import os; l = 20; print "%.2x" * l % tuple(map(ord, os.urandom(l)))'
+
 Configuring Optional Features
 -----------------------------
 
-The `development.ini` file has many options you can explore and configure.  It is geared towards development, so you will want to review
+The :file:`development.ini` file has many options you can explore and configure.  It is geared towards development, so you will want to review
 carefully and make changes for production use.
 
 To run SVN and Git services, see the :doc:`scm_host` page.

http://git-wip-us.apache.org/repos/asf/allura/blob/ccbd34f6/Allura/docs/getting_started/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/using.rst b/Allura/docs/getting_started/using.rst
index 84d9f93..a1952fa 100644
--- a/Allura/docs/getting_started/using.rst
+++ b/Allura/docs/getting_started/using.rst
@@ -23,6 +23,47 @@ Using Allura
 We don't have much end-user help for Allura yet.  SourceForge projects use Allura,
 though, so their support documentation may be useful to anyone using Allura:
 
+.. _what-are-neighborhoods:
+
+What are neighborhoods?
+-----------------------
+
+You can think of neighborhoods as groups of logically related projects, which all have the same default options. Allura has two default neighborhoods: "Projects" and "Users". The "Users" neighborhood is special, it contains a project for every user registered on a site. This user projects contain a few special tools, e.g. "Profile" and "Statistics".   The "Projects" contains all other projects.
+
+Each neighborhood has admin interface. You can get there by clicking "Neighborhood administration" from the home page of the neighborhood or by "Admin" icon in the top toolbar.
+
+This interface allows you to:
+
+- add a new project to the neighborhood
+- change neighborhood's name
+- change neighborhood icon
+- configure redirect from neighborhood's main page to other url
+- specify :ref:`project template <project-templates>` for newly created projects
+- specify project list url (the link will be displayed under neighborhood name in page header)
+- :ref:`anchor tools <anchored-tools>` in the top menu for each project in the neighborhood
+- :ref:`prohibit installation of specific tools <prohibited-tools>` in all projects of this neighborhood
+
+.. _project-templates:
+
+Project Templates
+^^^^^^^^^^^^^^^^^
+
+TODO
+
+.. _anchored-tools:
+
+Anchored Tools
+^^^^^^^^^^^^^^
+
+TODO
+
+.. _prohibited-tools:
+
+Prohibited Tools
+^^^^^^^^^^^^^^^^
+
+TODO
+
 
 Configuring your project
 ------------------------


[03/29] allura git commit: [#5943] ticket:815 Describe project templates

Posted by je...@apache.org.
[#5943] ticket:815 Describe project templates


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

Branch: refs/heads/ib/7685
Commit: 5f3e726e58dc021b623bd444fa7a56add7ff8f9d
Parents: a3d8785
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jul 10 12:29:12 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:37 2015 +0000

----------------------------------------------------------------------
 Allura/docs/getting_started/using.rst | 45 +++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/5f3e726e/Allura/docs/getting_started/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/using.rst b/Allura/docs/getting_started/using.rst
index e16c36f..1425dde 100644
--- a/Allura/docs/getting_started/using.rst
+++ b/Allura/docs/getting_started/using.rst
@@ -48,7 +48,50 @@ This interface allows you to:
 Project Templates
 ^^^^^^^^^^^^^^^^^
 
-TODO
+Allows to specify a template for newly created projects. The template controls default tools, permissions, labels, etc for a project. It is formatted as JSON dictionary with the following structure:
+
+.. code-block:: javascript
+
+  {
+    "private": false,
+    "tools": {
+        "tool_name": {               /* e.g. wiki, git, tickets, etc */
+          "label": "Tool Label",     /* Required */
+          "mount_point": "url-path"  /* Required */
+          "options": {}              /* Any other tool's options here. Optional */
+        }
+    },
+    "groups": [
+      {
+        "name": "Admin",        /* Default groups are: Admin, Developer, Member */
+        "usernames": ["admin1"] /* Add existing users to existing group */
+      },
+      {
+        "name": "New Group",     /* You can also create a new group */
+        "usernames": ["user1"],  /* and add existing users to it */
+        /*
+         * Then you need to specify permissions for newly created group.
+         * Supported permissions are: admin, create, update, read
+         */
+        "permissions": ["read", "update"]
+      }
+    ],
+    "tool_order": ["wiki", "tickets", "git"], /* tools order in the topbar menu */
+    "labels": ["Open Source", "web"],
+    "trove_cats": {
+      /*
+       * Available trove types are: root_database, license, developmentstatus,
+       * audience, os, language, topic, natlanguage, environment.
+       */
+      "trove_type": [905, 869]  /* TroveCategory ids */
+    },
+    "icon": {
+      "url: "http://img.host/path/to/image.png",  /* Required */
+      "filename": "default-project-icon.png"      /* Required */
+    }
+  }
+
+Top level keys are optional.
 
 .. _anchored-tools:
 


[27/29] allura git commit: [#7685] ticket:802 Use post in SCM subscription test and fix format call

Posted by je...@apache.org.
[#7685] ticket:802 Use post in SCM subscription test and fix format call


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

Branch: refs/heads/ib/7685
Commit: fea0123b954f0feae9f56c8554dc80a968342834
Parents: b6c9965
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jul 3 12:45:13 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 .../forgegit/tests/functional/test_controllers.py   | 16 +++++++++-------
 ForgeWiki/forgewiki/wiki_main.py                    |  2 +-
 2 files changed, 10 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/fea0123b/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index b84cd5a..20bfc1b 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -353,24 +353,26 @@ class TestRootController(_TestCase):
         assert link is not None, r.html
 
         # subscribe
-        self.app.get(ci + 'tree/subscribe?subscribe=True',
-                     extra_environ={'username': str(user.username)}).follow()
+        self.app.post(str(ci + 'tree/subscribe'),
+                      {'subscribe': True},
+                      extra_environ={'username': str(user.username)}).follow()
         # user is subscribed
         assert M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get(ci + 'tree/',
                          extra_environ={'username': str(user.username)})
-        link = r.html.find('a', 'artifact_unsubscribe active')
-        assert link is not None, r.html
+        inp = r.html.find('input', {'type': 'hidden', 'name': 'unsubscribe'})
+        assert inp is not None
 
         # unsubscribe
-        self.app.get(ci + 'tree/subscribe?unsubscribe=True',
+        self.app.post(str(ci + 'tree/subscribe'),
+                     {'unsubscribe': True},
                      extra_environ={'username': str(user.username)}).follow()
         # user is not subscribed
         assert not M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get(ci + 'tree/',
                          extra_environ={'username': str(user.username)})
-        link = r.html.find('a', 'artifact_subscribe')
-        assert link is not None, r.html
+        inp = r.html.find('input', {'type': 'hidden', 'name': 'subscribe'})
+        assert inp is not None
 
     def test_timezone(self):
         ci = self._get_ci()

http://git-wip-us.apache.org/repos/asf/allura/blob/fea0123b/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index c2bcb16..229a348 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -249,7 +249,7 @@ The wiki uses [Markdown](%s) syntax.
             subscribe_title = '{}{}'.format(
                 subscribe_action.capitalize(),
                 '' if subscribed else ' to wiki')
-            subscribe_url = '#toggle-subscribe'.format(self.url)
+            subscribe_url = '{}#toggle-subscribe'.format(self.url)
             links += [SitemapEntry(''),
                       SitemapEntry(subscribe_title, subscribe_url, ui_icon=g.icons['mail'])]
         if not admin_menu:


[17/29] allura git commit: [#7932] add test for git paged_diffs pagination

Posted by je...@apache.org.
[#7932] add test for git paged_diffs pagination


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

Branch: refs/heads/ib/7685
Commit: e1ad48033b356ebed24512d154e03514168dc890
Parents: 3017300
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Jul 21 20:02:10 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jul 21 20:25:39 2015 +0000

----------------------------------------------------------------------
 ForgeGit/forgegit/tests/model/test_repository.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/e1ad4803/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 4cfd2ef..9f007e6 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -693,6 +693,24 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         }
         assert_equals(diffs, expected)
 
+        # pagination
+        diffs = repo.paged_diffs('407950e8fba4dbc108ffbce0128ed1085c52cfd7', start=0, end=1)
+        expected = {
+            'added': [u'with space.txt'],
+            'removed': [],
+            'changed': [],
+            'total': 2,
+        }
+        assert_equals(diffs, expected)
+        diffs = repo.paged_diffs('407950e8fba4dbc108ffbce0128ed1085c52cfd7', start=1, end=2)
+        expected = {
+            'added': [u'привіт.txt'],
+            'removed': [],
+            'changed': [],
+            'total': 2,
+        }
+        assert_equals(diffs, expected)
+
     def test_merge_base(self):
         res = self.repo._impl.merge_base(self.merge_request)
         assert_equal(res, '1e146e67985dcd71c74de79613719bef7bddca4a')


[04/29] allura git commit: [#5943] ticket:815 Mention how to run paster-app w/o creating test data in INSTALL*.markown

Posted by je...@apache.org.
[#5943] ticket:815 Mention how to run paster-app w/o creating test data in INSTALL*.markown


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

Branch: refs/heads/ib/7685
Commit: 14ed0bde3f4677424d9adc005ac6561a5f0ecaf5
Parents: b677264
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 9 17:20:59 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:37 2015 +0000

----------------------------------------------------------------------
 INSTALL-docker.markdown | 4 ++++
 INSTALL.markdown        | 6 ++++++
 2 files changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/14ed0bde/INSTALL-docker.markdown
----------------------------------------------------------------------
diff --git a/INSTALL-docker.markdown b/INSTALL-docker.markdown
index 5330084..923b080 100644
--- a/INSTALL-docker.markdown
+++ b/INSTALL-docker.markdown
@@ -72,6 +72,10 @@ Initialize database with test data:
 
     ~$ docker-compose run web bash -c 'cd Allura && paster setup-app docker-dev.ini'
 
+If you want to skip test data creation you can instead run:
+
+    ~$ docker-compose run web bash -c 'cd Allura && ALLURA_TEST_DATA=False paster setup-app docker-dev.ini'
+
 Start containers in background:
 
     ~$ docker-compose up -d

http://git-wip-us.apache.org/repos/asf/allura/blob/14ed0bde/INSTALL.markdown
----------------------------------------------------------------------
diff --git a/INSTALL.markdown b/INSTALL.markdown
index d141988..361afb2 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -139,8 +139,14 @@ Allura uses a background task service called "taskd" to do async tasks like send
 
 In order to initialize the Allura database, you'll need to run the following:
 
+For development setup:
+
     (env-allura)~/src/allura/Allura$ paster setup-app development.ini
 
+For production setup:
+
+    (env-allura)~/src/allura/Allura$ ALLURA_TEST_DATA=False paster setup-app development.ini
+
 This shouldn't take too long, but it will start the taskd server doing tons of stuff in the background.  Once this is done, you can start the application server:
 
     (env-allura)~/src/allura/Allura$ nohup paster serve --reload development.ini  > /var/log/allura/allura.log 2>&1 &


[07/29] allura git commit: [#5943] some grammar improvements; add local TOC; fix INSTALL-docker capitalization

Posted by je...@apache.org.
[#5943] some grammar improvements; add local TOC; fix INSTALL-docker capitalization


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

Branch: refs/heads/ib/7685
Commit: f245736af57c81eb7f72323bc87767e8405f361b
Parents: e94786f
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Mon Jul 13 18:14:14 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:38 2015 +0000

----------------------------------------------------------------------
 Allura/docs/getting_started/using.rst | 25 ++++++++++++++-----------
 INSTALL.markdown                      |  2 +-
 2 files changed, 15 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/f245736a/Allura/docs/getting_started/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/using.rst b/Allura/docs/getting_started/using.rst
index 1425dde..b8fb6f3 100644
--- a/Allura/docs/getting_started/using.rst
+++ b/Allura/docs/getting_started/using.rst
@@ -19,27 +19,27 @@
 Using Allura
 ************
 
+.. contents::
+   :local:
 
-We don't have much end-user help for Allura yet.  SourceForge projects use Allura,
-though, so their support documentation may be useful to anyone using Allura:
 
 .. _what-are-neighborhoods:
 
 What are neighborhoods?
 -----------------------
 
-You can think of neighborhoods as groups of logically related projects, which all have the same default options. Allura has two default neighborhoods: "Projects" and "Users". The "Users" neighborhood is special, it contains a project for every user registered on a site. This user projects contain a few special tools, e.g. "Profile" and "Statistics".   The "Projects" contains all other projects.
+You can think of neighborhoods as groups of logically related projects, which all have the same default options. Allura has two default neighborhoods: "Projects" and "Users". The "Users" neighborhood is special, it contains a project for every user registered on a site. These user projects contain a few special tools, e.g. "Profile" and "Statistics".   The "Projects" neighborhood contains all other projects.
 
-Each neighborhood has admin interface. You can get there by clicking "Neighborhood administration" from the home page of the neighborhood or by "Admin" icon in the top toolbar.
+Each neighborhood has an admin interface. You can get there by clicking "Neighborhood administration" from the home page of the neighborhood or by "Admin" icon in the top toolbar.
 
 This interface allows you to:
 
 - add a new project to the neighborhood
-- change neighborhood's name
-- change neighborhood icon
+- change the neighborhood's name
+- change the neighborhood's icon
 - configure redirect from neighborhood's main page to other url
-- specify :ref:`project template <project-templates>` for newly created projects
-- specify project list url (the link will be displayed under neighborhood name in page header)
+- specify a :ref:`project template <project-templates>` for newly created projects
+- specify a project list url (the link will be displayed under neighborhood name in page header)
 - :ref:`anchor tools <anchored-tools>` in the top menu for each project in the neighborhood
 - :ref:`prohibit installation of specific tools <prohibited-tools>` in all projects of this neighborhood
 
@@ -48,7 +48,7 @@ This interface allows you to:
 Project Templates
 ^^^^^^^^^^^^^^^^^
 
-Allows to specify a template for newly created projects. The template controls default tools, permissions, labels, etc for a project. It is formatted as JSON dictionary with the following structure:
+Allows you to specify a template for newly created projects. The template controls default tools, permissions, labels, etc for a project.  If a template is specified, it is used during project creation and no tool choices are possible.  It is formatted as JSON dictionary with the following structure:
 
 .. code-block:: javascript
 
@@ -98,7 +98,7 @@ Top level keys are optional.
 Anchored Tools
 ^^^^^^^^^^^^^^
 
-Anchored tools allow you to "anchor" specific tools at the beginning of the topbar menu for all projects belonging to the neighborhood.  If specified tool does not exist in the project, it will be created automatically.  This tools can not be removed by the project.
+Anchored tools allow you to "anchor" specific tools at the beginning of the topbar menu for all projects belonging to the neighborhood.  If the specified tool does not exist in the project, it will be created automatically.  These tools can not be removed by the project.
 
 To configure them, go to "Neighborhood Admin -> Overview".  Use the following
 format "tool_name:The Label, another_tool:Another Label", e.g.
@@ -113,7 +113,7 @@ format "tool_name:The Label, another_tool:Another Label", e.g.
 Prohibited Tools
 ^^^^^^^^^^^^^^^^
 
-Prohibited tools allow you to forbid installation of specific tools for all the projects belonging to the neighborhood. Tools, already installed in the project, will not be automatically removed. To configure it, just list tool names using comma as separator. E.g.
+Prohibited tools allow you to forbid installation of specific tools for all the projects belonging to the neighborhood. Tools already installed in the project will not be automatically removed. To configure prohibited tools , just list tool names using comma as separator. E.g.
 
 .. code-block:: text
 
@@ -123,6 +123,9 @@ Prohibited tools allow you to forbid installation of specific tools for all the
 Configuring your project
 ------------------------
 
+We don't have much end-user help for Allura yet.  SourceForge projects use Allura,
+though, so their support documentation may be useful to anyone using Allura:
+
 See SourceForge help page: https://sourceforge.net/p/forge/documentation/Create%20a%20New%20Project/
 
 Note there are some SourceForge-specific references that don't apply to other Allura instances.

http://git-wip-us.apache.org/repos/asf/allura/blob/f245736a/INSTALL.markdown
----------------------------------------------------------------------
diff --git a/INSTALL.markdown b/INSTALL.markdown
index 361afb2..eab7dfd 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -19,7 +19,7 @@
 
 # Step-by-Step Installation
 
-For a simpler setup using Docker images, see INSTALL-Docker.markdown instead.
+For a simpler setup using Docker images, see INSTALL-docker.markdown instead.
 
 In these instructions, we'll use [VirtualBox](http://www.virtualbox.org) and [Ubuntu 14.04](http://ubuntu.com) (12.04 works too) to create a disposable sandbox for Allura development/testing.  Allura should work on other Linux systems (including OSX), but setting up all the dependencies will be different.
 


[18/29] allura git commit: [#7685] ticket:802 Require POST for repository subscription

Posted by je...@apache.org.
[#7685] ticket:802 Require POST for repository subscription


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

Branch: refs/heads/ib/7685
Commit: 41116b73071f8e4faf49c1c01f189df14b8775aa
Parents: 75cf25f
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Thu Jul 2 14:29:12 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:25 2015 +0300

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/41116b73/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index f8b0834..0178723 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -689,6 +689,7 @@ class TreeBrowser(BaseController, DispatchIndex):
             self), rest
 
     @expose()
+    @require_post()
     @validate(subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
         if subscribe:


[05/29] allura git commit: [#5943] ticket:815 Document anchored & prohibited tools

Posted by je...@apache.org.
[#5943] ticket:815 Document anchored & prohibited tools


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

Branch: refs/heads/ib/7685
Commit: a3d8785766f14b11d2e60a88feb9b9656480fb12
Parents: ccbd34f
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 9 19:10:37 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:37 2015 +0000

----------------------------------------------------------------------
 Allura/docs/getting_started/using.rst | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/a3d87857/Allura/docs/getting_started/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/using.rst b/Allura/docs/getting_started/using.rst
index a1952fa..e16c36f 100644
--- a/Allura/docs/getting_started/using.rst
+++ b/Allura/docs/getting_started/using.rst
@@ -55,14 +55,26 @@ TODO
 Anchored Tools
 ^^^^^^^^^^^^^^
 
-TODO
+Anchored tools allow you to "anchor" specific tools at the beginning of the topbar menu for all projects belonging to the neighborhood.  If specified tool does not exist in the project, it will be created automatically.  This tools can not be removed by the project.
+
+To configure them, go to "Neighborhood Admin -> Overview".  Use the following
+format "tool_name:The Label, another_tool:Another Label", e.g.
+
+.. code-block:: text
+
+    wiki:Wiki, activity:Activity
+
 
 .. _prohibited-tools:
 
 Prohibited Tools
 ^^^^^^^^^^^^^^^^
 
-TODO
+Prohibited tools allow you to forbid installation of specific tools for all the projects belonging to the neighborhood. Tools, already installed in the project, will not be automatically removed. To configure it, just list tool names using comma as separator. E.g.
+
+.. code-block:: text
+
+  blog, discussion, svn
 
 
 Configuring your project


[08/29] allura git commit: [#7540] ticket:816 Fix tests

Posted by je...@apache.org.
[#7540] ticket:816 Fix tests


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

Branch: refs/heads/ib/7685
Commit: b49a20bcfded19fd5b4859322ad250ae0c0837c8
Parents: fb7adce
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jul 8 15:37:43 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 20:01:44 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/model/test_discussion.py | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/b49a20bc/Allura/allura/tests/model/test_discussion.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_discussion.py b/Allura/allura/tests/model/test_discussion.py
index 1768777..c94f2a6 100644
--- a/Allura/allura/tests/model/test_discussion.py
+++ b/Allura/allura/tests/model/test_discussion.py
@@ -29,7 +29,7 @@ from pylons import tmpl_context as c
 from nose.tools import assert_equals, with_setup
 import mock
 from mock import patch
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_in
 
 from ming.orm import session, ThreadLocalORMSession
 from webob import exc
@@ -208,7 +208,11 @@ def test_attachment_methods():
     ThreadLocalORMSession.flush_all()
     n = M.Notification.query.get(
         subject=u'[test:wiki] Test comment notification')
-    assert '\nAttachment: fake.txt (37 Bytes; text/plain)' in n.text
+    url = h.absurl('{}attachment/{}'.format(p.url(), fs.filename))
+    assert_in(
+        '\nAttachments:\n\n'
+        '- [fake.txt]({}) (37 Bytes; text/plain)'.format(url),
+        n.text)
 
 
 @with_setup(setUp, tearDown())
@@ -265,11 +269,16 @@ def test_notification_two_attaches():
     fs2.filename = 'fake2.txt'
     fs2.type = 'text/plain'
     fs2.file = StringIO('this is the content of the fake file\n')
-    t.post(text=u'test message', forum=None, subject='', file_info=[fs1, fs2])
+    p = t.post(text=u'test message', forum=None, subject='', file_info=[fs1, fs2])
     ThreadLocalORMSession.flush_all()
     n = M.Notification.query.get(
         subject=u'[test:wiki] Test comment notification')
-    assert '\nAttachment: fake.txt (37 Bytes; text/plain)  fake2.txt (37 Bytes; text/plain)' in n.text
+    base_url = h.absurl('{}attachment/'.format(p.url()))
+    assert_in(
+        '\nAttachments:\n\n'
+        '- [fake.txt]({0}fake.txt) (37 Bytes; text/plain)\n'
+        '- [fake2.txt]({0}fake2.txt) (37 Bytes; text/plain)'.format(base_url),
+        n.text)
 
 
 @with_setup(setUp, tearDown)


[11/29] allura git commit: [#7928] allow wide results to do horizontal scroll

Posted by je...@apache.org.
[#7928] allow wide results to do horizontal scroll


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

Branch: refs/heads/ib/7685
Commit: de47627f779bce0d0ea50107d8ab503857ac374e
Parents: b49a20b
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Mon Jul 13 17:09:09 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Tue Jul 14 10:57:33 2015 +0300

----------------------------------------------------------------------
 Allura/allura/templates/site_admin_search.html | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/de47627f/Allura/allura/templates/site_admin_search.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_search.html b/Allura/allura/templates/site_admin_search.html
index 108b39b..c43982d 100644
--- a/Allura/allura/templates/site_admin_search.html
+++ b/Allura/allura/templates/site_admin_search.html
@@ -34,9 +34,11 @@
   </div>
 
   {% if objects %}
-    <table>
-      {% include search_results_template %}
-    </table>
+    <div style="overflow:auto; clear:both">
+      <table>
+        {% include search_results_template %}
+      </table>
+    </div>
   {% endif %}
 
   <div class="grid-23">


[23/29] allura git commit: [#7685] ticket:802 Fixed subscription to whole wiki

Posted by je...@apache.org.
[#7685] ticket:802 Fixed subscription to whole wiki


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

Branch: refs/heads/ib/7685
Commit: 18e32fe3d2c8475ba71cb64178cc94bb2646ccc0
Parents: 41116b7
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Thu Jul 2 16:26:09 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 ForgeWiki/forgewiki/templates/wiki/page_view.html | 12 +++++++++++-
 ForgeWiki/forgewiki/wiki_main.py                  |  4 +++-
 2 files changed, 14 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/18e32fe3/ForgeWiki/forgewiki/templates/wiki/page_view.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_view.html b/ForgeWiki/forgewiki/templates/wiki/page_view.html
index 7289f23..7a6135e 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_view.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_view.html
@@ -116,12 +116,22 @@
       {% endif %}
   {% endif %}
   {{c.confirmation.display(content='')}}
+  {% if not c.user.is_anonymous() %}
+    <form action="{{c.app.url}}subscribe" id="wiki_subscribe_form" style="display:none;" method="POST">
+      {{lib.csrf_token()}}
+      {% if c.subscribed %}
+        <input type="hidden" name="unsubscribe" value="1">
+      {% else %}
+        <input type="hidden" name="subscribe" value="1">
+      {% endif %}
+    </form>
+  {% endif %}
 {% endblock %}
 
 {% block wiki_extra_js %}
   <script type="text/javascript">
     $('a[href$="#toggle-subscribe"]').click(function() {
-      $('#subscribe_form').submit();
+      $('#wiki_subscribe_form').submit();
       return false;
     })
   </script>

http://git-wip-us.apache.org/repos/asf/allura/blob/18e32fe3/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 229b217..c2bcb16 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -249,7 +249,7 @@ The wiki uses [Markdown](%s) syntax.
             subscribe_title = '{}{}'.format(
                 subscribe_action.capitalize(),
                 '' if subscribed else ' to wiki')
-            subscribe_url = '{}#toggle-subscribe'.format(self.url, subscribe_action)
+            subscribe_url = '#toggle-subscribe'.format(self.url)
             links += [SitemapEntry(''),
                       SitemapEntry(subscribe_title, subscribe_url, ui_icon=g.icons['mail'])]
         if not admin_menu:
@@ -483,6 +483,8 @@ class PageController(BaseController, FeedController):
         if self.page is not None:
             self.attachment = WikiAttachmentsController(self.page)
         c.create_page_lightbox = W.create_page_lightbox
+        if not c.user.is_anonymous():
+            c.subscribed = M.Mailbox.subscribed()
 
     def _check_security(self):
         if self.page:


[14/29] allura git commit: [#7927] strengthen / document tests a bit

Posted by je...@apache.org.
[#7927] strengthen / document tests a bit


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

Branch: refs/heads/ib/7685
Commit: bbeebdbb9a3059f1c4595203967e2dbb6bd3a7a6
Parents: 82a3642
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Jul 21 17:43:23 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jul 21 17:44:31 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/functional/test_rest.py | 18 ++++++++++++++++++
 Allura/allura/tests/test_middlewares.py     |  1 +
 2 files changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/bbeebdbb/Allura/allura/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest.py b/Allura/allura/tests/functional/test_rest.py
index 3fed49c..903cda7 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -353,6 +353,24 @@ class TestRestHome(TestRestApiBase):
             r = self.api_get('/rest/p/test/')
             assert r.status_int == 404
 
+    @td.with_wiki
+    def test_cors_POST_req_blocked_by_csrf(self):
+        # so test-admin isn't automatically logged in for all requests
+        self.app.extra_environ = {'disable_auth_magic': 'True'}
+
+        # regular login to get a session cookie set up
+        r = self.app.get('/auth/')
+        r.form['username'] = 'test-admin'
+        r.form['password'] = 'foo'
+        r.form.submit()
+
+        # simulate CORS ajax request withCredentials (cookie headers)
+        # make sure we don't allow the cookies to authorize the request (else could be a CSRF attack vector)
+        assert self.app.cookies['allura']
+        self.app.post('/rest/p/test/wiki/NewPage', headers={'Origin': 'http://bad.com/'},
+                      status=401)
+
+
 class TestDoap(TestRestApiBase):
     validate_skip = True
     ns = '{http://usefulinc.com/ns/doap#}'

http://git-wip-us.apache.org/repos/asf/allura/blob/bbeebdbb/Allura/allura/tests/test_middlewares.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_middlewares.py b/Allura/allura/tests/test_middlewares.py
index 4b45097..58dc515 100644
--- a/Allura/allura/tests/test_middlewares.py
+++ b/Allura/allura/tests/test_middlewares.py
@@ -77,6 +77,7 @@ class TestCORSMiddleware(object):
         exc.HTTPOk.return_value.assert_called_once_with(env, callback)
 
     def test_get_response_headers_simple(self):
+        # Allow-Origin: * is crucial for security, since that prevents browsers from exposing results fetched withCredentials: true (aka cookies)
         assert_equal(self.cors.get_response_headers(),
                      [('Access-Control-Allow-Origin', '*')])
         assert_equal(self.cors.get_response_headers(preflight=False),


[09/29] allura git commit: [#7540] ticket:816 Show attachment links for comment

Posted by je...@apache.org.
[#7540] ticket:816 Show attachment links for comment


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

Branch: refs/heads/ib/7685
Commit: fb7adce380ce6a600203ff0fc1abe7bbef120f9f
Parents: 9d7b26e
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jul 8 15:00:45 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 20:01:44 2015 +0000

----------------------------------------------------------------------
 Allura/allura/model/notification.py | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/fb7adce3/Allura/allura/model/notification.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/notification.py b/Allura/allura/model/notification.py
index ca1a850..01482f0 100644
--- a/Allura/allura/model/notification.py
+++ b/Allura/allura/model/notification.py
@@ -157,15 +157,18 @@ class Notification(MappedClass):
             text = kwargs.get('text') or post.text
             file_info = kwargs.pop('file_info', None)
             if file_info is not None:
-                text = "%s\n\n\nAttachment:" % text
+                text = "%s\n\n\nAttachments:\n" % text
                 if not isinstance(file_info, list):
                     file_info = [file_info]
                 for attach in file_info:
                     attach.file.seek(0, 2)
                     bytecount = attach.file.tell()
                     attach.file.seek(0)
-                    text = "%s %s (%s; %s) " % (
-                        text, attach.filename, h.do_filesizeformat(bytecount), attach.type)
+                    url = h.absurl('{}attachment/{}'.format(
+                        post.url(), h.urlquote(attach.filename)))
+                    text = "%s\n- [%s](%s) (%s; %s)" % (
+                        text, attach.filename, url,
+                        h.do_filesizeformat(bytecount), attach.type)
 
             subject = post.subject or ''
             if post.parent_id and not subject.lower().startswith('re:'):


[28/29] allura git commit: [#7685] ticket:802 Add trailing slash to discussion controller

Posted by je...@apache.org.
[#7685] ticket:802 Add trailing slash to discussion controller


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

Branch: refs/heads/ib/7685
Commit: 0ec05d547e540d0ef1ac34318ad28f209be16e02
Parents: fea0123
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jul 3 12:53:50 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 Allura/allura/controllers/discuss.py | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0ec05d54/Allura/allura/controllers/discuss.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/discuss.py b/Allura/allura/controllers/discuss.py
index 13fe436..b329b01 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -89,6 +89,7 @@ class DiscussionController(BaseController, FeedController):
         if not hasattr(self, 'moderate'):
             self.moderate = ModerationController(self)
 
+    @with_trailing_slash
     @expose('jinja:allura:templates/discussion/index.html')
     def index(self, threads=None, limit=None, page=0, count=0, **kw):
         c.discussion = self.W.discussion


[19/29] allura git commit: [#7685] ticket:802 Submit subscription request via POST

Posted by je...@apache.org.
[#7685] ticket:802 Submit subscription request via POST


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

Branch: refs/heads/ib/7685
Commit: cc33817a3e151554c2b6446f3afb7c052760129d
Parents: e1ad480
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Wed Jun 24 23:39:53 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:25 2015 +0300

----------------------------------------------------------------------
 Allura/allura/templates/widgets/subscribe.html       | 13 +++++++++++--
 ForgeBlog/forgeblog/main.py                          |  1 +
 ForgeDiscussion/forgediscussion/controllers/forum.py |  1 +
 ForgeTracker/forgetracker/tracker_main.py            |  2 ++
 ForgeWiki/forgewiki/wiki_main.py                     |  2 ++
 5 files changed, 17 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/cc33817a/Allura/allura/templates/widgets/subscribe.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/subscribe.html b/Allura/allura/templates/widgets/subscribe.html
index a88c7e4..5154eb7 100644
--- a/Allura/allura/templates/widgets/subscribe.html
+++ b/Allura/allura/templates/widgets/subscribe.html
@@ -16,9 +16,14 @@
        specific language governing permissions and limitations
        under the License.
 -#}
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
 {% if value %}
   {% if style == 'icon' %}
-    <a href="{{action}}?unsubscribe=True" class="artifact_unsubscribe active" title="Unsubscribe from this {{thing}}"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Unsubscribe from this {{thing}}"></b></a>
+    <form action="{{action}}" id="subscribe_form" style="display:inline;" method="POST">
+      {{lib.csrf_token()}}
+      <input type="hidden" name="unsubscribe" value="1">
+      <a href="#" class="artifact_unsubscribe active" title="Unsubscribe from this {{thing}}" onclick="$('#subscribe_form').submit(); return false;"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Unsubscribe from this {{thing}}"></b></a>
+    </form>
   {% else %}
     You are currently subscribed to this {{thing}}.
     <br/>
@@ -26,7 +31,11 @@
   {% endif %}
 {% else %}
   {% if style == 'icon' %}
-    <a href="{{action}}?subscribe=True" class="artifact_subscribe" title="Subscribe to this {{thing}}"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Subscribe to this {{thing}}"></b></a>
+    <form action="{{action}}" id="subscribe_form" style="display:inline;" method="POST">
+      {{lib.csrf_token()}}
+      <input type="hidden" name="subscribe" value="1">
+      <a href="#" class="artifact_subscribe" title="Subscribe to this {{thing}}" onclick="$('#subscribe_form').submit(); return false;"><b data-icon="{{g.icons['mail'].char}}" class="ico {{g.icons['mail'].css}}" title="Subscribe to this {{thing}}"></b></a>
+    </form>
   {% else %}
     You are currently not subscribed to this {{thing}}.
     <br/>

http://git-wip-us.apache.org/repos/asf/allura/blob/cc33817a/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index edfe1f0..9007f87 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -376,6 +376,7 @@ class PostController(BaseController, FeedController):
         redirect('.')
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
         if subscribe:

http://git-wip-us.apache.org/repos/asf/allura/blob/cc33817a/ForgeDiscussion/forgediscussion/controllers/forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/forum.py b/ForgeDiscussion/forgediscussion/controllers/forum.py
index 67dbea3..94fe6d9 100644
--- a/ForgeDiscussion/forgediscussion/controllers/forum.py
+++ b/ForgeDiscussion/forgediscussion/controllers/forum.py
@@ -123,6 +123,7 @@ class ForumController(DiscussionController):
         return dict()
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe_to_forum(self, subscribe=None, unsubscribe=None, shortname=None, **kw):
         if subscribe:

http://git-wip-us.apache.org/repos/asf/allura/blob/cc33817a/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 68b1a0c..c3a83e6 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1091,6 +1091,7 @@ class RootController(BaseController, FeedController):
         )
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None):
         if subscribe:
@@ -1520,6 +1521,7 @@ class TicketController(BaseController, FeedController):
         redirect('.')
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None):
         if subscribe:

http://git-wip-us.apache.org/repos/asf/allura/blob/cc33817a/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index cfaec31..7899ffa 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -464,6 +464,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
         return dict(example=MARKDOWN_EXAMPLE)
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None):
         if subscribe:
@@ -735,6 +736,7 @@ class PageController(BaseController, FeedController):
         redirect(request.referer)
 
     @expose()
+    @require_post()
     @validate(W.subscribe_form)
     def subscribe(self, subscribe=None, unsubscribe=None, **kw):
         if not self.page:


[02/29] allura git commit: [#5943] ticket:815 Improve root user creation in setup-app

Posted by je...@apache.org.
[#5943] ticket:815 Improve root user creation in setup-app


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

Branch: refs/heads/ib/7685
Commit: e94786f3e7dcfd0eca65ec5642d655449775ed05
Parents: 5f3e726
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jul 10 12:34:07 2015 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Jul 13 18:14:37 2015 +0000

----------------------------------------------------------------------
 Allura/allura/websetup/bootstrap.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/e94786f3/Allura/allura/websetup/bootstrap.py
----------------------------------------------------------------------
diff --git a/Allura/allura/websetup/bootstrap.py b/Allura/allura/websetup/bootstrap.py
index 37e250e..33a3f3c 100644
--- a/Allura/allura/websetup/bootstrap.py
+++ b/Allura/allura/websetup/bootstrap.py
@@ -119,20 +119,20 @@ def bootstrap(command, conf, vars):
     if create_test_data:
         root = create_user('Root', make_project=False)
     else:
-        # TODO: ask user to provide username/password for root
+        from getpass import getpass
         root_name = raw_input('Enter username for root user (default "root"): ').strip()
         if not root_name:
             root_name = 'root'
         ok = False
         while not ok:
-            root_password1 = raw_input('Enter password: ').strip()
-            root_password2 = raw_input('Confirm password: ').strip()
+            root_password1 = getpass('Enter password: ')
             if len(root_password1) == 0:
-                log.info('Please, provide password')
-                return
+                log.info('Password must not be empty')
+                continue
+            root_password2 = getpass('Confirm password: ')
             if root_password1 != root_password2:
                 log.info("Passwords don't match")
-                return
+                continue
             root = create_user(root_name, password=root_password1, make_project=False)
             ok = True
 


[13/29] allura git commit: [#7927] ticket:821 Add tests for CORSMiddleware

Posted by je...@apache.org.
[#7927] ticket:821 Add tests for CORSMiddleware


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

Branch: refs/heads/ib/7685
Commit: 6625703b123d070d813f6ddf0dce05de729e15b2
Parents: 0c71798
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 16 18:01:13 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Jul 16 18:01:13 2015 +0300

----------------------------------------------------------------------
 Allura/allura/tests/test_middlewares.py | 106 +++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/6625703b/Allura/allura/tests/test_middlewares.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_middlewares.py b/Allura/allura/tests/test_middlewares.py
new file mode 100644
index 0000000..1ca6d8a
--- /dev/null
+++ b/Allura/allura/tests/test_middlewares.py
@@ -0,0 +1,106 @@
+#       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.
+
+from mock import MagicMock, patch
+from datadiff.tools import assert_equal
+from nose.tools import assert_not_equal
+from allura.lib.custom_middleware import CORSMiddleware
+
+
+class TestCORSMiddleware(object):
+
+    def setUp(self):
+        self.app = MagicMock()
+        self.allowed_methods = ['GET', 'POST', 'DELETE']
+        self.allowed_headers = ['Authorization', 'Accept']
+        self.cors = CORSMiddleware(
+            self.app,
+            self.allowed_methods,
+            self.allowed_headers)
+
+    def test_init(self):
+        cors = CORSMiddleware(self.app, ['get', 'post'], ['Some-Header'])
+        assert_equal(cors.app, self.app)
+        assert_equal(cors.allowed_methods, ['GET', 'POST'])
+        assert_equal(cors.allowed_headers, set(['some-header']))
+
+    def test_call_not_api_request(self):
+        callback = MagicMock()
+        env = {'PATH_INFO': '/p/test/'}
+        self.cors(env, callback)
+        self.app.assert_called_once_with(env, callback)
+
+    def test_call_invalid_cors(self):
+        callback = MagicMock()
+        env = {'PATH_INFO': '/rest/p/test/'}
+        self.cors(env, callback)
+        self.app.assert_called_once_with(env, callback)
+
+    def test_handle_call_simple_request(self):
+        callback = MagicMock()
+        env = {'PATH_INFO': '/rest/p/test/',
+               'HTTP_ORIGIN': 'my.site.com',
+               'REQUEST_METHOD': 'GET'}
+        self.cors(env, callback)
+        assert_equal(self.app.call_count, 1)
+        assert_equal(self.app.call_args_list[0][0][0], env)
+        assert_not_equal(self.app.call_args_list[0][0][1], callback)
+
+    @patch('allura.lib.custom_middleware.exc', autospec=True)
+    def test_handle_call_preflight_request(self, exc):
+        callback = MagicMock()
+        env = {'PATH_INFO': '/rest/p/test/',
+               'HTTP_ORIGIN': 'my.site.com',
+               'REQUEST_METHOD': 'OPTIONS',
+               'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'POST'}
+        self.cors(env, callback)
+        assert_equal(self.app.call_count, 0)
+        exc.HTTPOk.assert_called_once_with(headers=[
+            ('Access-Control-Allow-Origin', '*'),
+            ('Access-Control-Allow-Methods', 'GET, POST, DELETE'),
+            ('Access-Control-Allow-Headers', 'accept, authorization')
+        ])
+        exc.HTTPOk.return_value.assert_called_once_with(env, callback)
+
+    def test_get_response_headers_simple(self):
+        assert_equal(self.cors.get_response_headers(),
+                     [('Access-Control-Allow-Origin', '*')])
+        assert_equal(self.cors.get_response_headers(preflight=False),
+                     [('Access-Control-Allow-Origin', '*')])
+
+    def test_get_response_headers_preflight(self):
+        assert_equal(
+            self.cors.get_response_headers(preflight=True),
+            [('Access-Control-Allow-Origin', '*'),
+             ('Access-Control-Allow-Methods', 'GET, POST, DELETE'),
+             ('Access-Control-Allow-Headers', 'accept, authorization')])
+
+    def test_get_response_headers_preflight_with_cache(self):
+        cors = CORSMiddleware(self.app, ['GET', 'PUT'], ['Accept'], 86400)
+        assert_equal(cors.get_response_headers(preflight=True),
+                     [('Access-Control-Allow-Origin', '*'),
+                      ('Access-Control-Allow-Methods', 'GET, PUT'),
+                      ('Access-Control-Allow-Headers', 'accept'),
+                      ('Access-Control-Max-Age', 86400)])
+
+    def test_get_access_control_request_headers(self):
+        key = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'
+        f = self.cors.get_access_control_request_headers
+        assert_equal(f({}), set())
+        assert_equal(f({key: ''}), set())
+        assert_equal(f({key: 'Authorization, Accept'}),
+                     set(['authorization', 'accept']))


[16/29] allura git commit: [#7932] Fix pagination issue in the commit browser

Posted by je...@apache.org.
[#7932] Fix pagination issue in the commit browser


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

Branch: refs/heads/ib/7685
Commit: 30173000e217fe5756aa9aff34c372ac1db0b4b7
Parents: bbeebdb
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Thu Jul 16 15:13:33 2015 -0400
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jul 21 20:25:39 2015 +0000

----------------------------------------------------------------------
 ForgeGit/forgegit/model/git_repo.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/30173000/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 7d87a09..9549fa6 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -652,13 +652,14 @@ class GitImplementation(M.RepositoryImplementation):
             # relies on this)
             '-t',
             '-z',  # don't escape filenames and use \x00 as fields delimiter
-            commit_id)
-        files = files.split('\x00')[:-1]
-        # files = ['A', 'filename', 'D', 'another filename', ...]
+            commit_id).split('\x00')[:-1]
+
         total = len(files) / 2
-        for i in range(1, len(files), 2):
-            status = files[i-1]
-            name = h.really_unicode(files[i])
+        files = [(files[i], h.really_unicode(files[i+1]))
+                 for i in xrange(0, len(files), 2)]
+
+        # files = [('A', u'filename'), ('D', u'another filename'), ...]
+        for status, name in files[start:end]:
             if status == 'A':
                 added.append(name)
             elif status == 'D':


[29/29] allura git commit: [#7685] ticket:825 Add UI feedback for subscribe/unsubscribe actions

Posted by je...@apache.org.
[#7685] ticket:825 Add UI feedback for subscribe/unsubscribe actions


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

Branch: refs/heads/ib/7685
Commit: dd55af37a1b6e4b64e9aa9c0c5b2659f43316e76
Parents: 65526b4
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jul 22 17:10:45 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 17:10:45 2015 +0300

----------------------------------------------------------------------
 .../lib/widgets/resources/js/subscriptions.js   | 56 ++++++++++++++++----
 Allura/allura/templates/widgets/subscribe.html  |  1 +
 ForgeBlog/forgeblog/main.py                     |  2 +
 .../forgediscussion/controllers/forum.py        |  1 +
 ForgeTracker/forgetracker/tracker_main.py       |  2 +
 ForgeWiki/forgewiki/wiki_main.py                |  2 +
 6 files changed, 53 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/Allura/allura/lib/widgets/resources/js/subscriptions.js
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/resources/js/subscriptions.js b/Allura/allura/lib/widgets/resources/js/subscriptions.js
index c03bbeb..d5a7648 100644
--- a/Allura/allura/lib/widgets/resources/js/subscriptions.js
+++ b/Allura/allura/lib/widgets/resources/js/subscriptions.js
@@ -23,21 +23,30 @@ var dom = React.createElement;
 var state = {
   'thing': 'tool',
   'subscribed': false,
+  'subscribed_to_tool': false,
   'url': '',
   'icon': {}
 };
 
 SubscriptionForm = React.createClass({
 
+  getInitialState: function() {
+    return {tooltip_timeout: null};
+  },
+
   render: function() {
     var action = this.props.subscribed ? "Unsubscribe from" : "Subscribe to";
     var title = action + ' this ' + this.props.thing;
     var link_opts = {
+      ref: 'link',
       className: this.props.subscribed ? 'active' : '',
       href: '#',
       title: title,
       onClick: this.handleClick
     };
+    if (this.props.in_progress) {
+      link_opts.style = {cursor: 'wait'};
+    }
     var icon_opts = {
       'data-icon': this.props.icon.char,
       className: 'ico ' + this.props.icon.css,
@@ -56,23 +65,48 @@ SubscriptionForm = React.createClass({
       data.subscribe = true;
     }
     set_state({in_progress: true});
-    /*
-     * TODO:
-     * - show 'in-progress' status to user somehow
-     * - handle errors (show to the user in some kind of popup/flash?)
-     * - If user is subscribed to the whole tool and she tries to subsribe to
-     *   the artifact she will not be subscribed, so nothing will change in the
-     *   UI and it's confusing. We need to show some information message in
-     *   such case
-     */
     $.post(url, data, function(resp) {
       if (resp.status == 'ok') {
-        set_state({subscribed: resp.subscribed});
+        set_state({
+          subscribed: resp.subscribed,
+          subscribed_to_tool: resp.subscribed_to_tool
+        });
+        var link = this.getLinkNode();
+        var text = null;
+        if (resp.subscribed_to_tool) {
+          text = "You can't subscribe to this ";
+          text += this.props.thing;
+          text += " because you are already subscribed to the entire tool";
+        } else {
+          var action = resp.subscribed ? 'subscribed to' : 'unsubscribed from';
+          text = 'Successfully ' + action + ' this ' + this.props.thing;
+        }
+        $(link).tooltipster('content', text).tooltipster('show');
+        if (this.state.tooltip_timeout) {
+          clearTimeout(this.state.tooltip_timeout);
+        }
+        var t = setTimeout(function() { $(link).tooltipster('hide'); }, 4000);
+        this.setState({tooltip_timeout: t});
       }
-    }).always(function() {
+    }.bind(this)).always(function() {
       set_state({in_progress: false});
     });
     return false;
+  },
+
+  getLinkNode: function() { return React.findDOMNode(this.refs.link); },
+
+  componentDidMount: function() {
+    var link = this.getLinkNode();
+    $(link).tooltipster({
+      content: '',
+      animation: 'fade',
+      delay: 200,
+      trigger: 'custom',
+      position: 'top',
+      iconCloning: false,
+      maxWidth: 300
+    });
   }
 
 });

http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/Allura/allura/templates/widgets/subscribe.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/subscribe.html b/Allura/allura/templates/widgets/subscribe.html
index 1361a9c..2b61f71 100644
--- a/Allura/allura/templates/widgets/subscribe.html
+++ b/Allura/allura/templates/widgets/subscribe.html
@@ -31,6 +31,7 @@
   document.SUBSCRIPTION_OPTIONS = {
     "thing": "{{thing}}",
     "subscribed": {{"true" if value else "false"}},
+    "subscribed_to_tool": {{"true" if tool_subscribed else "false"}},
     "url": "{{action}}",
     "icon": {
       "char": "{{g.icons['mail'].char}}",

http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index adfc172..b4f4641 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -323,6 +323,7 @@ class PostController(BaseController, FeedController):
         post = self._get_version(version)
         base_post = self.post
         subscribed = M.Mailbox.subscribed(artifact=self.post)
+        c.subscribe_form.tool_subscribed = M.Mailbox.subscribed()
         return dict(post=post, base_post=base_post,
                     page=page, limit=limit, count=post_count,
                     subscribed=subscribed)
@@ -388,6 +389,7 @@ class PostController(BaseController, FeedController):
         return {
             'status': 'ok',
             'subscribed': M.Mailbox.subscribed(artifact=self.post),
+            'subscribed_to_tool': M.Mailbox.subscribed(),
         }
 
     def get_feed(self, project, app, user):

http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/ForgeDiscussion/forgediscussion/controllers/forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/forum.py b/ForgeDiscussion/forgediscussion/controllers/forum.py
index b915823..8d0117d 100644
--- a/ForgeDiscussion/forgediscussion/controllers/forum.py
+++ b/ForgeDiscussion/forgediscussion/controllers/forum.py
@@ -133,6 +133,7 @@ class ForumController(DiscussionController):
         return {
             'status': 'ok',
             'subscribed': M.Mailbox.subscribed(artifact=self.discussion),
+            'subscribed_to_tool': M.Mailbox.subscribed(),
         }
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 0049957..ddf570d 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1359,6 +1359,7 @@ class TicketController(BaseController, FeedController):
             c.ticket_custom_field = W.ticket_custom_field
             c.vote_form = W.vote_form
             tool_subscribed = M.Mailbox.subscribed()
+            c.subscribe_form.tool_subscribed = tool_subscribed
             if tool_subscribed:
                 subscribed = False
             else:
@@ -1534,6 +1535,7 @@ class TicketController(BaseController, FeedController):
         return {
             'status': 'ok',
             'subscribed': M.Mailbox.subscribed(artifact=self.ticket),
+            'subscribed_to_tool': M.Mailbox.subscribed(),
         }
 
     @expose('json:')

http://git-wip-us.apache.org/repos/asf/allura/blob/dd55af37/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 9fca1c2..513b1e2 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -558,6 +558,7 @@ class PageController(BaseController, FeedController):
         next = cur + 1
         hide_left_bar = not (c.app.show_left_bar)
         subscribed_to_page = M.Mailbox.subscribed(artifact=self.page)
+        c.subscribe_form.tool_subscribed = M.Mailbox.subscribed()
         return dict(
             page=page,
             cur=cur, prev=prev, next=next,
@@ -750,6 +751,7 @@ class PageController(BaseController, FeedController):
         return {
             'status': 'ok',
             'subscribed': M.Mailbox.subscribed(artifact=self.page),
+            'subscribed_to_tool': M.Mailbox.subscribed(),
         }
 
 


[20/29] allura git commit: [#7685] ticket:802 Subsribe via POST in wiki sidebar

Posted by je...@apache.org.
[#7685] ticket:802 Subsribe via POST in wiki sidebar


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

Branch: refs/heads/ib/7685
Commit: c5f8bdad548216d2579bc05261e70b3fd63abfd4
Parents: cc33817
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Thu Jun 25 14:24:05 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:25 2015 +0300

----------------------------------------------------------------------
 ForgeWiki/forgewiki/templates/wiki/page_view.html | 10 +++++++++-
 ForgeWiki/forgewiki/wiki_main.py                  |  2 +-
 2 files changed, 10 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/c5f8bdad/ForgeWiki/forgewiki/templates/wiki/page_view.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_view.html b/ForgeWiki/forgewiki/templates/wiki/page_view.html
index 0420ecf..7289f23 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_view.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_view.html
@@ -116,5 +116,13 @@
       {% endif %}
   {% endif %}
   {{c.confirmation.display(content='')}}
-
 {% endblock %}
+
+{% block wiki_extra_js %}
+  <script type="text/javascript">
+    $('a[href$="#toggle-subscribe"]').click(function() {
+      $('#subscribe_form').submit();
+      return false;
+    })
+  </script>
+{% endblock wiki_extra_js %}

http://git-wip-us.apache.org/repos/asf/allura/blob/c5f8bdad/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 7899ffa..229b217 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -249,7 +249,7 @@ The wiki uses [Markdown](%s) syntax.
             subscribe_title = '{}{}'.format(
                 subscribe_action.capitalize(),
                 '' if subscribed else ' to wiki')
-            subscribe_url = '{}subscribe?{}=True'.format(self.url, subscribe_action)
+            subscribe_url = '{}#toggle-subscribe'.format(self.url, subscribe_action)
             links += [SitemapEntry(''),
                       SitemapEntry(subscribe_title, subscribe_url, ui_icon=g.icons['mail'])]
         if not admin_menu:


[15/29] allura git commit: [#7927] pass cache setting through; use str in headers

Posted by je...@apache.org.
[#7927] pass cache setting through; use str in headers


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

Branch: refs/heads/ib/7685
Commit: 82a3642b3c36839e7fcbbc9ebce43fad4eb6a531
Parents: 6625703
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Jul 21 17:42:55 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Jul 21 17:44:31 2015 +0000

----------------------------------------------------------------------
 Allura/allura/config/middleware.py      | 2 +-
 Allura/allura/lib/custom_middleware.py  | 2 +-
 Allura/allura/tests/test_middlewares.py | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/82a3642b/Allura/allura/config/middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index 922034d..6c39d4b 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -143,7 +143,7 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
         allowed_methods = aslist(config.get('cors.methods'))
         allowed_headers = aslist(config.get('cors.headers'))
         cache_duration = asint(config.get('cors.cache_duration', 0))
-        app = CORSMiddleware(app, allowed_methods, allowed_headers)
+        app = CORSMiddleware(app, allowed_methods, allowed_headers, cache_duration)
     # Setup the allura SOPs
     app = allura_globals_middleware(app)
     # Ensure http and https used per config

http://git-wip-us.apache.org/repos/asf/allura/blob/82a3642b/Allura/allura/lib/custom_middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/custom_middleware.py b/Allura/allura/lib/custom_middleware.py
index 4eef689..e56a530 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -133,7 +133,7 @@ class CORSMiddleware(object):
             ])
             if self.cache_preflight:
                 headers.append(
-                    ('Access-Control-Max-Age', self.cache_preflight)
+                    ('Access-Control-Max-Age', str(self.cache_preflight))
                 )
         return headers
 

http://git-wip-us.apache.org/repos/asf/allura/blob/82a3642b/Allura/allura/tests/test_middlewares.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_middlewares.py b/Allura/allura/tests/test_middlewares.py
index 1ca6d8a..4b45097 100644
--- a/Allura/allura/tests/test_middlewares.py
+++ b/Allura/allura/tests/test_middlewares.py
@@ -95,7 +95,7 @@ class TestCORSMiddleware(object):
                      [('Access-Control-Allow-Origin', '*'),
                       ('Access-Control-Allow-Methods', 'GET, PUT'),
                       ('Access-Control-Allow-Headers', 'accept'),
-                      ('Access-Control-Max-Age', 86400)])
+                      ('Access-Control-Max-Age', '86400')])
 
     def test_get_access_control_request_headers(self):
         key = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'


[26/29] allura git commit: [#7685] ticket:819 Fix tests failing due to new markup

Posted by je...@apache.org.
[#7685] ticket:819 Fix tests failing due to new markup


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

Branch: refs/heads/ib/7685
Commit: 65526b4681378feaa2a9762d92ee3ea558b1f082
Parents: 100d91d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Jul 13 12:32:15 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py             | 16 ++++++++++++
 .../tests/functional/test_controllers.py        | 26 +++++++++++---------
 .../forgetracker/tests/functional/test_root.py  | 25 ++++++++++---------
 .../forgewiki/tests/functional/test_rest.py     |  2 +-
 .../forgewiki/tests/functional/test_root.py     |  2 --
 5 files changed, 44 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/65526b46/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 4a8f983..5a61ab7 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -18,6 +18,7 @@
 """Unit and functional test suite for allura."""
 import os
 import urllib
+import json
 
 import mock
 import beaker.session
@@ -169,6 +170,21 @@ class TestController(object):
         "Extract webflash content from response."
         return urllib.unquote(response.cookies_set.get('webflash', ''))
 
+    def subscription_options(self, response):
+        """
+        Extract subscription options to be passed to React SubscriptionForm
+        component from the <script> tag
+        """
+        script = None
+        for s in response.html.findAll('script'):
+            if s.getText().strip().startswith('document.SUBSCRIPTION_OPTIONS'):
+                script = s
+                break
+        assert script is not None, 'subscription options not found'
+        _, json_dict = script.getText().split('=')
+        json_dict = json_dict.strip(' ;')
+        return json.loads(json_dict)
+
 
 class TestRestApiBase(TestController):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/65526b46/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 20bfc1b..7f6bf88 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -349,30 +349,32 @@ class TestRootController(_TestCase):
         assert not M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get(ci + 'tree/',
                          extra_environ={'username': str(user.username)})
-        link = r.html.find('a', 'artifact_subscribe')
-        assert link is not None, r.html
+        opts = self.subscription_options(r)
+        assert_equal(opts['subscribed'], False)
 
         # subscribe
-        self.app.post(str(ci + 'tree/subscribe'),
-                      {'subscribe': True},
-                      extra_environ={'username': str(user.username)}).follow()
+        r = self.app.post(str(ci + 'tree/subscribe'),
+                          {'subscribe': True},
+                          extra_environ={'username': str(user.username)})
+        assert_equal(r.json, {'status': 'ok', 'subscribed': True})
         # user is subscribed
         assert M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get(ci + 'tree/',
                          extra_environ={'username': str(user.username)})
-        inp = r.html.find('input', {'type': 'hidden', 'name': 'unsubscribe'})
-        assert inp is not None
+        opts = self.subscription_options(r)
+        assert_equal(opts['subscribed'], True)
 
         # unsubscribe
-        self.app.post(str(ci + 'tree/subscribe'),
-                     {'unsubscribe': True},
-                     extra_environ={'username': str(user.username)}).follow()
+        r = self.app.post(str(ci + 'tree/subscribe'),
+                          {'unsubscribe': True},
+                          extra_environ={'username': str(user.username)})
+        assert_equal(r.json, {'status': 'ok', 'subscribed': False})
         # user is not subscribed
         assert not M.Mailbox.subscribed(user_id=user._id)
         r = self.app.get(ci + 'tree/',
                          extra_environ={'username': str(user.username)})
-        inp = r.html.find('input', {'type': 'hidden', 'name': 'subscribe'})
-        assert inp is not None
+        opts = self.subscription_options(r)
+        assert_equal(opts['subscribed'], False)
 
     def test_timezone(self):
         ci = self._get_ci()

http://git-wip-us.apache.org/repos/asf/allura/blob/65526b46/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 23c77f4..de061ca 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -294,7 +294,8 @@ class TestFunctionalController(TrackerTestController):
         summary = 'test new ticket'
         ticket_view = self.new_ticket(summary=summary).follow()
         assert_true(summary in ticket_view)
-        assert 'class="artifact_subscribe' in ticket_view
+        opts = self.subscription_options(ticket_view)
+        assert_equal(opts['subscribed'], False)
 
     def test_labels(self):
         ticket_view = self.new_ticket(summary='summary', labels="test label").follow()
@@ -840,7 +841,7 @@ class TestFunctionalController(TrackerTestController):
         }, upload_files=[upload]).follow()
         assert file_name in ticket_editor, ticket_editor.showbrowser()
         req = self.app.get('/bugs/1/')
-        file_link = req.html.findAll('form')[2].findAll('a')[6]
+        file_link = req.html.findAll('form')[1].findAll('a')[6]
         assert_equal(file_link.string, file_name)
         self.app.post(str(file_link['href']), {
             'delete': 'True'
@@ -882,7 +883,7 @@ class TestFunctionalController(TrackerTestController):
         ticket_editor = self.app.post('/bugs/1/update_ticket', {
             'summary': 'zzz'
         }, upload_files=[upload]).follow()
-        download = self.app.get(str(ticket_editor.html.findAll('form')[2].findAll('a')[7]['href']))
+        download = self.app.get(str(ticket_editor.html.findAll('form')[1].findAll('a')[7]['href']))
         assert_equal(download.body, file_data)
 
     def test_two_attachments(self):
@@ -1239,7 +1240,7 @@ class TestFunctionalController(TrackerTestController):
         # Test edit ticket form
         self.new_ticket(summary='Test ticket')
         response = self.app.get('/bugs/1/')
-        form = response.forms[2]
+        form = response.forms[1]
         assert_equal(
             form['ticket_form.custom_fields._priority'].value, 'normal')
         assert_equal(form['ticket_form.custom_fields._category'].value, '')
@@ -1247,9 +1248,9 @@ class TestFunctionalController(TrackerTestController):
         form['ticket_form.custom_fields._priority'] = 'urgent'
         form['ticket_form.custom_fields._category'] = 'bugs'
         error_form = form.submit()
-        assert_equal(error_form.forms[2]['ticket_form.custom_fields._priority'].value,
+        assert_equal(error_form.forms[1]['ticket_form.custom_fields._priority'].value,
                      'urgent')
-        assert_equal(error_form.forms[2]['ticket_form.custom_fields._category'].value,
+        assert_equal(error_form.forms[1]['ticket_form.custom_fields._category'].value,
                      'bugs')
 
     def test_new_ticket_validation(self):
@@ -1269,7 +1270,7 @@ class TestFunctionalController(TrackerTestController):
         # set a summary, submit, and check for success
         error_form.forms[1]['ticket_form.summary'] = summary
         success = error_form.forms[1].submit().follow().html
-        assert success.findAll('form')[2].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
+        assert success.findAll('form')[1].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
         assert success.find('input', {'name': 'ticket_form.summary'})['value'] == summary
 
     def test_edit_ticket_validation(self):
@@ -1280,7 +1281,7 @@ class TestFunctionalController(TrackerTestController):
         # check that existing form is valid
         assert response.html.find('input', {'name': 'ticket_form.summary'})['value'] == old_summary
         assert not response.html.find('div', {'class': 'error'})
-        form = response.forms[2]
+        form = response.forms[1]
         # try submitting with no summary set and check for error message
         form['ticket_form.summary'] = ""
         error_form = form.submit()
@@ -1289,11 +1290,11 @@ class TestFunctionalController(TrackerTestController):
         assert error_message.string == 'You must provide a Title'
         assert error_message.findPreviousSibling('input').get('name') == 'ticket_form.summary'
         # set a summary, submit, and check for success
-        error_form.forms[2]['ticket_form.summary'] = new_summary
-        r = error_form.forms[2].submit()
+        error_form.forms[1]['ticket_form.summary'] = new_summary
+        r = error_form.forms[1].submit()
         assert r.status_int == 302, r.showbrowser()
         success = r.follow().html
-        assert success.findAll('form')[2].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
+        assert success.findAll('form')[1].get('action') == '/p/test/bugs/1/update_ticket_from_widget'
         assert success.find('input', {'name': 'ticket_form.summary'})['value'] == new_summary
 
     def test_home(self):
@@ -2720,7 +2721,7 @@ class TestCustomUserField(TrackerTestController):
     def test_change_user_field(self):
         kw = {'custom_fields._code_review': ''}
         r = self.new_ticket(summary='test custom fields', **kw).follow()
-        f = r.forms[2]
+        f = r.forms[1]
         # Populate ProjectUserCombo's select with option we want.
         # This is a workaround for tests,
         # in real enviroment this is populated via ajax.

http://git-wip-us.apache.org/repos/asf/allura/blob/65526b46/ForgeWiki/forgewiki/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/tests/functional/test_rest.py b/ForgeWiki/forgewiki/tests/functional/test_rest.py
index a2b58ae..6a5de3b 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_rest.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_rest.py
@@ -42,7 +42,7 @@ class TestWikiApi(TestRestApiBase):
 
     def test_get_page(self):
         r = self.app.get('/p/test/wiki/Home/')
-        discussion_url = r.html.findAll('form')[3]['action'][:-4]
+        discussion_url = r.html.findAll('form')[2]['action'][:-4]
         content = file(__file__).read()
         self.app.post('/wiki/Home/attach',
                       upload_files=[('file_info', 'test_root.py', content)])

http://git-wip-us.apache.org/repos/asf/allura/blob/65526b46/ForgeWiki/forgewiki/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/tests/functional/test_root.py b/ForgeWiki/forgewiki/tests/functional/test_root.py
index dafb74f..3f69d89 100644
--- a/ForgeWiki/forgewiki/tests/functional/test_root.py
+++ b/ForgeWiki/forgewiki/tests/functional/test_root.py
@@ -258,8 +258,6 @@ class TestRootController(TestController):
                 'labels': '',
                 'viewable_by-0.id': 'all'})
         self.app.post('/wiki/tést/revert', params=dict(version='1'))
-        response = self.app.get('/wiki/tést/')
-        assert 'Subscribe' in response
         response = self.app.get('/wiki/tést/diff?v1=0&v2=0')
         assert 'tést' in response
         d = dict(title='testdiff', text="""**Optionally**, you may also want to remove all the unused accounts that have accumulated (one was created for *every* logged in SF-user who has visited your MediaWiki hosted app):


[25/29] allura git commit: [#7685] ticket:802 Added @with_trailing_slash to discussion thread controller

Posted by je...@apache.org.
[#7685] ticket:802 Added @with_trailing_slash to discussion thread controller


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

Branch: refs/heads/ib/7685
Commit: b6c9965d8a533b6bc9a7c0209b06cf738dbc67e0
Parents: 18e32fe
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Thu Jul 2 16:32:16 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Jul 22 15:35:26 2015 +0300

----------------------------------------------------------------------
 Allura/allura/controllers/discuss.py | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/b6c9965d/Allura/allura/controllers/discuss.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/discuss.py b/Allura/allura/controllers/discuss.py
index 28cfcb6..13fe436 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -20,6 +20,7 @@ from datetime import datetime
 import logging
 
 from tg import expose, redirect, validate, request, flash
+from tg.decorators import with_trailing_slash
 from pylons import tmpl_context as c, app_globals as g
 from webob import exc
 
@@ -182,6 +183,7 @@ class ThreadController(BaseController, FeedController):
         id = unquote(id)
         return self.PostController(self._discussion_controller, self.thread, id), remainder
 
+    @with_trailing_slash
     @expose('jinja:allura:templates/discussion/thread.html')
     def index(self, limit=None, page=0, count=0, **kw):
         c.thread = self.W.thread