• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

allura


Commit MetaInfo

Revisionc4109f62d8436ef507061d932ad94a41fac5637e (tree)
Zeit2012-06-28 07:33:48
AutorCory Johns <johnsca@geek...>
CommiterCory Johns

Log Message

[#4388] Added UI for watching / following projects / users

Signed-off-by: Cory Johns <johnsca@geek.net>

Ändern Zusammenfassung

Diff

--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -122,6 +122,8 @@ class Globals(object):
122122 user=Icon('U', 'ico-user'),
123123 secure=Icon('(', 'ico-lock'),
124124 unsecure=Icon(')', 'ico-unlock'),
125+ star=Icon('S', 'ico-star'),
126+ watch=Icon('E', 'ico-watch'),
125127 # Permissions
126128 perm_read=Icon('E', 'ico-focus'),
127129 perm_update=Icon('0', 'ico-sync'),
--- /dev/null
+++ b/ForgeActivity/forgeactivity/config/resources.py
@@ -0,0 +1,5 @@
1+import pkg_resources
2+
3+def register_ew_resources(manager):
4+ manager.register_directory(
5+ 'activity_js', pkg_resources.resource_filename('forgeactivity', 'widgets/resources/js'))
--- a/ForgeActivity/forgeactivity/main.py
+++ b/ForgeActivity/forgeactivity/main.py
@@ -8,13 +8,21 @@ from pylons import c, response
88 from tg import expose, validate, config, redirect
99 from tg.decorators import with_trailing_slash
1010 from paste.deploy.converters import asbool
11+from formencode import validators as fev
12+from webob import exc
1113
1214 from allura.app import Application
1315 from allura import version
1416 from allura.controllers import BaseController
17+from allura.lib.security import require_authenticated
18+
19+from activitystream import director
20+
21+from .widgets.follow import FollowToggle
1522
1623 log = logging.getLogger(__name__)
1724
25+
1826 class ForgeActivityApp(Application):
1927 """Project Activity page for projects."""
2028 __version__ = version.__version__
@@ -43,6 +51,8 @@ class ForgeActivityApp(Application):
4351 def uninstall(self, project):
4452 pass # pragma no cover
4553
54+class W:
55+ follow_toggle = FollowToggle()
4656
4757 class ForgeActivityController(BaseController):
4858 @expose('jinja:forgeactivity:templates/index.html')
@@ -50,6 +60,36 @@ class ForgeActivityController(BaseController):
5060 def index(self, **kw):
5161 activity_enabled = asbool(config.get('activity_stream.enabled', False))
5262 if not activity_enabled:
53- response.status = 404
54- return dict()
55- return dict()
63+ raise HTTPNotFound()
64+
65+ c.follow_toggle = W.follow_toggle
66+ followee = c.project
67+ if c.project.is_user_project:
68+ followee = c.project.user_project_of
69+ following = director().is_connected(c.user, followee)
70+ return dict(following=following)
71+
72+ @expose('json:')
73+ @validate(W.follow_toggle)
74+ def follow(self, follow, **kw):
75+ activity_enabled = asbool(config.get('activity_stream.enabled', False))
76+ if not activity_enabled:
77+ raise HTTPNotFound()
78+
79+ require_authenticated()
80+ followee = c.project
81+ if c.project.is_user_project:
82+ followee = c.project.user_project_of
83+ try:
84+ if follow:
85+ director().connect(c.user, followee)
86+ else:
87+ director().disconnect(c.user, followee)
88+ except Exception as e:
89+ return dict(
90+ success=False,
91+ message='Unexpected error: %s' % e)
92+ return dict(
93+ success=True,
94+ message=W.follow_toggle.success_message(follow),
95+ following=follow)
--- a/ForgeActivity/forgeactivity/templates/index.html
+++ b/ForgeActivity/forgeactivity/templates/index.html
@@ -3,7 +3,20 @@
33
44 {% block title %}{{c.project.name}} Activity{% endblock %}
55
6-{% block header %}{{c.project.name}} Activity{% endblock %}
6+{% block header %}
7+ Activity for
8+ {% if c.project.is_user_project %}
9+ {{c.project.user_project_of.display_name}}
10+ {% else %}
11+ {{c.project.name}}
12+ {% endif %}
13+{% endblock %}
14+
15+{% block actions %}
16+ {% if c.user and c.user != c.user.anonymous() %}
17+ {{c.follow_toggle.display(following=following)}}
18+ {% endif %}
19+{% endblock %}
720
821 {% block content %}
922 <div class="grid-14">
--- /dev/null
+++ b/ForgeActivity/forgeactivity/templates/widgets/follow.html
@@ -0,0 +1,5 @@
1+<a href="{{action}}?follow={{not following}}"
2+ class="artifact_follow{{ ' active' if following }}"
3+ title="{{'Stop %sing' % action_label if following else action_label|capitalize}} {{thing}}"><b
4+ data-icon="{{g.icons[icon].char}}" class="ico {{g.icons[icon].css}}"
5+ title="{{'Stop %sing' % action_label if following else action_label|capitalize}} {{thing}}"></b></a>
--- /dev/null
+++ b/ForgeActivity/forgeactivity/widgets/follow.py
@@ -0,0 +1,42 @@
1+from pylons import c
2+from formencode import validators as fev
3+import ew as ew_core
4+import ew.jinja2_ew as ew
5+
6+
7+class FollowToggle(ew.SimpleForm):
8+ template='jinja:forgeactivity:templates/widgets/follow.html'
9+ defaults=dict(
10+ ew.SimpleForm.defaults,
11+ thing='project',
12+ action='follow',
13+ action_label='watch',
14+ icon='watch',
15+ following=False)
16+
17+ class fields(ew_core.NameList):
18+ follow = ew.HiddenField(validator=fev.StringBool())
19+
20+ def resources(self):
21+ yield ew.JSLink('activity_js/follow.js')
22+
23+ def prepare_context(self, context):
24+ default_context = super(FollowToggle, self).prepare_context({})
25+ if c.project.is_user_project:
26+ default_context.update(
27+ thing=c.project.user_project_of.display_name,
28+ action_label='follow',
29+ icon='star',
30+ )
31+ else:
32+ default_context.update(thing=c.project.name)
33+ default_context.update(context)
34+ return default_context
35+
36+ def success_message(self, following):
37+ context = self.prepare_context({})
38+ return u'You are {state} {action}ing {thing}.'.format(
39+ state='now' if following else 'no longer',
40+ action=context['action_label'],
41+ thing=context['thing'],
42+ )
--- /dev/null
+++ b/ForgeActivity/forgeactivity/widgets/resources/js/follow.js
@@ -0,0 +1,34 @@
1+$(document).ready(function() {
2+ function title_stop_following($elem) {
3+ $elem.attr('title', $elem.attr('title').replace(/^([A-Z])(\w+)/, function(p,c,w) {
4+ return 'Stop ' + c.toLowerCase() + w + 'ing';
5+ }));
6+ }
7+
8+ function title_start_following($elem) {
9+ $elem.attr('title', $elem.attr('title').replace(/^Stop ([a-z])(\w+)ing/, function(p,c,w) {
10+ return c.toUpperCase() + w;
11+ }));
12+ }
13+
14+ $('.artifact_follow').click(function(e) {
15+ e.preventDefault();
16+ var $link = $(this);
17+ $.get(this.href, function(result) {
18+ flash(result.message, result.success ? 'success' : 'error');
19+ console.log(result.following);
20+ if (result.following && !$link.hasClass('active')) {
21+ $link.attr('href', $link.attr('href').replace(/True$/i, 'False'));
22+ $link.addClass('active');
23+ title_stop_following($link);
24+ title_stop_following($link.find('b'));
25+ } else if (!result.following && $link.hasClass('active')) {
26+ $link.attr('href', $link.attr('href').replace(/False$/i, 'True'));
27+ $link.removeClass('active');
28+ title_start_following($link);
29+ title_start_following($link.find('b'));
30+ }
31+ });
32+ return false;
33+ });
34+});
--- a/ForgeActivity/setup.py
+++ b/ForgeActivity/setup.py
@@ -22,5 +22,8 @@ setup(name='ForgeActivity',
2222 # -*- Entry points: -*-
2323 [allura]
2424 activity=forgeactivity.main:ForgeActivityApp
25+
26+ [easy_widgets.resources]
27+ ew_resources=forgeactivity.config.resources:register_ew_resources
2528 """,
2629 )