You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2013/03/21 15:22:14 UTC

[38/48] git commit: [5453] Allowing to hide stats

[5453] Allowing to hide stats


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

Branch: refs/heads/si/5453
Commit: fa9f88ba2adb35c9b2594e4e6328742d9e6f707e
Parents: 6abbf9d
Author: Stefano Invernizzi <st...@apache.org>
Authored: Sat Feb 16 13:01:52 2013 +0100
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Thu Mar 21 14:20:55 2013 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py                  |   22 ++
 Allura/allura/ext/user_profile/user_main.py        |    2 +-
 Allura/allura/lib/plugin.py                        |    8 +
 Allura/allura/lib/widgets/forms.py                 |   16 +
 Allura/allura/model/contrib_stats.py               |    1 +
 Allura/allura/templates/user_preferences.html      |  287 +++++++++++++++
 .../forgeuserstats/controllers/userstats.py        |   11 +
 .../forgeuserstats/templates/artifacts.html        |   15 +-
 .../forgeuserstats/templates/commits.html          |   15 +-
 ForgeUserStats/forgeuserstats/templates/index.html |   15 +-
 .../forgeuserstats/templates/tickets.html          |   15 +-
 11 files changed, 401 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 2914799..a60b25d 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -58,6 +58,7 @@ class F(object):
     remove_inactive_period_form = forms.RemoveInactivePeriodForm()
     save_skill_form = forms.AddUserSkillForm()
     remove_skill_form = forms.RemoveSkillForm()
+    set_statistics = forms.StatsPreferencesForm()
 
 class AuthController(BaseController):
 
@@ -726,6 +727,27 @@ class SubscriptionsController(BaseController):
     @h.vardec
     @expose()
     @require_post()
+    @validate(F.set_statistics, error_handler=index)
+    def set_statistics(self, **kw):
+        require_authenticated()
+        c.user.stats.visible = kw.get('visible', True)
+        flash('Your preferences about statistics were successfully updated!')
+        redirect('.#Statistics')
+
+    @expose()
+    @require_post()
+    def upload_sshkey(self, key=None):
+        ap = plugin.AuthenticationProvider.get(request)
+        try:
+            ap.upload_sshkey(c.user.username, key)
+        except AssertionError, ae:
+            flash('Error uploading key: %s' % ae, 'error')
+        flash('Key uploaded')
+        redirect('.')
+
+    @h.vardec
+    @expose()
+    @require_post()
     @validate(F.subscription_form, error_handler=index)
     def update_subscriptions(self, subscriptions=None, **kw):
         for s in subscriptions:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/ext/user_profile/user_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index 759f3f3..f3c3331 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -64,7 +64,7 @@ class UserProfileController(BaseController):
         user = c.project.user_project_of
         if not user:
             raise exc.HTTPNotFound()
-        if g.show_userstats:
+        if g.show_userstats and user.stats.visible:
             from forgeuserstats.main import ForgeUserStatsApp
             link, description = ForgeUserStatsApp.createlink(user)
         else:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 2c8038f..84e9f6e 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -738,6 +738,14 @@ class ThemeProvider(object):
         return RemoveInactivePeriodForm()
 
     @LazyProperty
+    def statistics_form(self):
+        '''
+        :return: None, or an easywidgets Form to render on the user preferences page
+        '''
+        from allura.lib.widgets.forms import StatsPreferencesForm
+        return StatsPreferencesForm(action='/auth/prefs/set_statistics')
+
+    @LazyProperty
     def add_trove_category(self):
         '''
         :return: None, or an easywidgets Form to render on the page to create a

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/lib/widgets/forms.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/forms.py b/Allura/allura/lib/widgets/forms.py
index f76eff3..c1a1d75 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -451,6 +451,22 @@ class RemoveTimeSlotForm(ForgeForm):
         d['endtime'] = V.convertTime(kw.get('endtime',''))
         return d
 
+
+class StatsPreferencesForm(ForgeForm):
+    defaults=dict(ForgeForm.defaults)
+
+    class fields(ew_core.NameList):
+        visible = ew.Checkbox(
+            label='Make my personal statistics visible to other users.')
+            
+    def display(self, **kw):
+        if kw.get('user').stats.visible:
+            self.fields['visible'].attrs = {'checked':'true'}      
+        else:
+            self.fields['visible'].attrs = {}    
+        return super(ForgeForm, self).display(**kw)
+                
+
 class RemoveTroveCategoryForm(ForgeForm):
     defaults=dict(ForgeForm.defaults, submit_text=None, show_errors=False)
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/model/contrib_stats.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/contrib_stats.py b/Allura/allura/model/contrib_stats.py
index 8a71d86..d9cada2 100644
--- a/Allura/allura/model/contrib_stats.py
+++ b/Allura/allura/model/contrib_stats.py
@@ -21,6 +21,7 @@ class Stats(MappedClass):
 
     _id=FieldProperty(S.ObjectId)
 
+    visible = FieldProperty(bool, if_missing = True)
     registration_date = FieldProperty(datetime)
     general = FieldProperty([dict(
         category = S.ObjectId,

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/Allura/allura/templates/user_preferences.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_preferences.html b/Allura/allura/templates/user_preferences.html
new file mode 100644
index 0000000..8159f52
--- /dev/null
+++ b/Allura/allura/templates/user_preferences.html
@@ -0,0 +1,287 @@
+{% set hide_left_bar = True %}
+{% extends g.theme.master %}
+
+{% block title %}{{c.user.username}} / Preferences{% endblock %}
+
+{% block header %}User Preferences for {{c.user.username}}{% endblock %}
+
+{% block content %}
+  <ul id="account-nav-menu" class="b-hornav droppy">
+      {% for item in menu -%}
+      <li id="{{ item.tabid }}">
+      <a href="{{ item.target }}">
+          {{ item.title }}
+          <div class="marker{% if item.target.rstrip('/') == request.path.rstrip('/') %} current{% endif %}"></div>
+      </a>
+      </li>
+      {%- endfor %}
+   </ul>
+
+  <div style="clear:both" class="grid-20">
+    <h2>Personal Settings</h2>
+    {{g.theme.personal_data_form.display(action="/auth/prefs/change_personal_data", user=c.user)}}
+  </div>
+
+  <div style="clear:both" class="grid-20">
+    <a name="Contacts"></a>
+    <h2>Personal Contacts</h2>
+    <h3>Skype account</h3>
+
+    {{g.theme.skype_account_form.display(action="/auth/prefs/skype_account",
+          initial_value=c.user.get_pref('skypeaccount'))}}
+
+    {%if c.user.get_pref('socialnetworks') or c.user.get_pref('telnumbers') or c.user.get_pref('webpages') %}
+      <h3>Other existing contacts</h3>
+        <table>
+          <tr>
+            <thead>
+              <th>Type</th>
+              <th>Contact</th>
+              <th>Actions</th>
+            </thead>
+          </tr>
+          {% for sn in c.user.get_pref('socialnetworks') %}
+             {{g.theme.remove_socialnetwork_form.display(account=sn.accounturl, socialnetwork=sn.socialnetwork)}}
+          {% endfor %}
+
+          {% for tn in c.user.get_pref('telnumbers') %}
+              {{g.theme.remove_textvalue_form.display(action="/auth/prefs/remove_telnumber", value=tn, label="Telephone number")}}
+          {%endfor%}
+
+          {% for ws in c.user.get_pref('webpages') %}
+              {{g.theme.remove_textvalue_form.display(action="/auth/prefs/remove_webpage", value=ws, label="Website url")}}
+          {%endfor%}
+        </table>
+    {% endif %}
+
+    <h3>Add a social network account</h3>
+    {{g.theme.add_socialnetwork_form.display(action="/auth/prefs/add_social_network")}}
+    <h3>Add a telephone number</h3>
+    {{g.theme.add_telnumber_form.display(action="/auth/prefs/add_telnumber")}}
+    <h3>Add a personal website</h3>
+    {{g.theme.add_website_form.display(action="/auth/prefs/add_webpage")}}
+  </div>
+
+  <a name="Availability"></a>
+  <div style="clear:both" class="grid-20">
+    <h2>Availability</h2>
+    <div class="grid-18">
+      If you want, you can set the weekly timeslot during which you are usually available to support other users of the forge.
+      Please, set your time intervals choosing a weekday and entering the time interval according to the timezone specified in your
+      personal data, using the format HH:MM. If you didn't set any timezone, your timeslots could be meaningless to other users,
+      therefore they will be ignored.
+    </div>
+    <div class="grid-18">
+      You can also specify periods of time during which you won't be able to work on the forge, in orther to communicate other users
+      that they can't contact you during those days. Please, do it specifying date intervals in format DD/MM/YYYY.
+    </div>
+  </div>
+  <div class="grid-20">
+    {%if c.user.get_availability_timeslots() %}
+      <h3>Existing availability timeslots</h3>
+      <table>
+        <tr>
+          <thead>
+            <th>Weekday</th>
+            <th>Start time</th>
+            <th>End time</th>
+            <th>Actions</th>
+          </thead>
+        </tr>
+        {% for ts in c.user.get_availability_timeslots() %}
+          {{g.theme.remove_timeslot_form.display(
+                action="/auth/prefs/remove_timeslot",
+                weekday=ts.week_day,
+                starttime=ts.start_time,
+                endtime=ts.end_time)}}
+        {%endfor%}
+      </table>
+    {% endif %}
+    <h3>Add a new availability timeslot</h3>
+    {{g.theme.add_timeslot_form.display(action="/auth/prefs/add_timeslot")}}
+  </div>
+
+  <div class="grid-20">
+    {%if c.user.get_inactive_periods() %}
+      <h3>Existing periods of inactivity on the forge</h3>
+      <table>
+        <tr>
+          <thead>
+            <th>Start date</th>
+            <th>End date</th>
+            <th>Actions</th>
+          </thead>
+        </tr>
+        {% for ip in c.user.get_inactive_periods() %}
+          {{g.theme.remove_inactive_period_form.display(
+                action="/auth/prefs/remove_inactive_period",
+                startdate=ip.start_date,
+                enddate=ip.end_date)}}
+        {%endfor%}
+      </table>
+    {% endif %}
+    <h3>Add a new period of inactivity on the forge</h3>
+    {{g.theme.add_inactive_period_form.display(action="/auth/prefs/add_inactive_period")}}
+  </div>
+
+  <div class="grid-20">
+    <h2>Skills list</h2>
+    <ul><li><a href="/auth/prefs/user_skills">Click here to check and change your skills list</a></li></ul>
+  </div>
+
+  {% if g.show_userstats %}
+    <a name="Statistics"></a>
+    <div class="grid-20">
+      <h2>Contribution statistics</h2>
+      {{g.theme.statistics_form.display(user=c.user)}}
+    </div>
+  {% endif %}
+
+  {% if g.theme.password_change_form %}
+  <div class="grid-20">
+    <h2>Change Password</h2>
+    {{ g.theme.password_change_form.display() }}
+  </div>
+  {% endif %}
+  {% if g.theme.upload_key_form %}
+  <div class="grid-20">
+    <h2>Upload ssh public key</h2>
+    {{ g.theme.upload_key_form.display() }}
+  </div>
+  {% endif %}
+
+  {% if tg.config.get('auth.method', 'local') == 'local' %}
+      <br style="clear:both"/>
+      <h2>API Token</h2>
+      {% if api_token %}
+        <p>
+          <b>API Key:</b><br/>
+          {{api_token.api_key}}<br/>
+          <b>Secret Key:</b><br/>
+          {{api_token.secret_key}}<br/>
+        </p>
+        <form method="POST" action="del_api_token" class="grid-18">
+          <input type="submit" value="Delete API Token">
+        </form>
+      {% else %}
+        <p>No API token generated</p>
+      {% endif %}
+      <form method="POST" action="gen_api_token" class="grid-18">
+        <input type="submit" value="(Re)generate API Token">
+      </form>
+  {% endif %}
+
+  <div style="clear:both"></div>
+
+  <h2>Authorized Third-party Applications</h2>
+  {% for access_tok in authorized_applications %}
+    <div>
+      <h3>{{access_tok.consumer_token.name}}</h3>
+      {{access_tok.consumer_token.description_html}}
+      {{ c.revoke_access.display(value=access_tok) }}
+      <br style="clear:both"/>
+  </div>
+ {% endfor %}
+    {% if not authorized_applications %}<p>No authorized third-party applications</p>{% endif %}
+
+
+  <h2>Subscriptions</h2>
+  {% if subscriptions %}
+    <p><em>Mark tools that you want to subscribe to. Unmark tools that you want to unsubscribe from. Press 'Save' button.</em></p>
+    {{c.form.display(action='update_subscriptions', value=dict(subscriptions=subscriptions))}}
+  {% else%}
+    <p>No subscriptions.</p>
+  {% endif %}
+  <hr/>
+  <div style="clear:both">&nbsp;</div>
+  <form action="update" method="post">
+        {% if tg.config.get('auth.method', 'local') == 'local' %}
+        <label class="grid-4">Display Name</label>
+        <div class="grid-18">
+          <input name="preferences.display_name" value="{{c.user.display_name}}" type="text">
+        </div>
+        {% endif %}
+        <label class="grid-4">Email Format</label>
+        <div class="grid-18">
+          <select name="preferences.email_format">
+            <option value="plain" {{'selected' if c.user.preferences.email_format == 'plain' else ''}}>Plain Text</option>
+            <option value="html" {{'selected' if c.user.preferences.email_format == 'html' else ''}}>HTML</option>
+            <option value="both" {{'selected' if c.user.preferences.email_format == 'both' else ''}}>Combined</option>
+          </select>
+        </div>
+        {% if tg.config.get('auth.method', 'local') == 'local' %}
+        <label class="grid-4">Page Size</label>
+        <div class="grid-18">
+          <select name="preferences.results_per_page">
+            {% for per_page in [25, 50, 100, 250] %}
+                <option {% if per_page == c.user.preferences.results_per_page %}selected="selected"{% endif %}
+                   value="{{per_page}}">{{per_page}}</option>
+            {% endfor %}
+          </select>
+        </div>
+        {% endif %}
+
+    {% if tg.config.get('auth.method', 'local') == 'local' %}
+      {% for a in c.user.email_addresses %}
+        <input name="addr-{{loop.index0}}.ord" value="{{loop.index0}}" type="hidden"/>
+      {% endfor %}
+      {% if c.user.email_addresses %}
+        <h3 class="grid-18">Email Addresses</h3>
+        <table class="grid-18">
+          <tr>
+            <th>Primary?</th>
+            <th>Address</th>
+            <th>Confirmed</th>
+            <th></th>
+          </tr>
+          {% for a in c.user.email_addresses %}
+          <tr>
+            {% set obj = c.user.address_object(a) %}
+            <td>{{lib.radio_button('primary_addr', None, a, c.user.preferences.email_address)}}</td>
+            <td>{{a}}</td>
+            {% if obj %}
+            <td>
+              {% if obj.confirmed %}
+                yes
+              {% else %}
+                no (<a href="{{g.url('/auth/send_verification_link', a=a)}}">verify</a>)
+              {% endif %}
+            </td>
+            {% else %}
+              <td>Unknown addr obj {{a}}</td>
+            {% endif %}
+            <td>{{lib.submit_button('Delete', 'addr-%s.delete' % i)}}</td>
+          </tr>
+          {% endfor %}
+        </table>
+        {% endif %}
+        <div class="grid-18">
+        {{lib.text_field('new_addr.addr', 'New Email Address')}}
+        {{lib.submit_button('Claim Address', name='new_addr.claim')}}
+        </div>
+
+        {% if c.user.open_ids %}
+        <h3 class="grid-18">OpenIDs Claimed</h3>
+        <table class="grid-18">
+          <tr>
+            <th>OpenID</th>
+            <th></th>
+          </tr>
+          {% for oid in c.user.open_ids %}
+            {% set obj = c.user.openid_object(oid) %}
+          <tr>
+            <td>{{oid}}</td>
+            <td>{{lib.submit_button('Delete', 'oid-%s.delete' % loop.index0)}}</td>
+          </tr>
+          {% endfor %}
+        </table>
+        {% endif %}
+        <div class="grid-18">
+        <a href="/auth/claim_oid">Claim New OpenID</a>
+        </div>
+    {% endif %}
+    <div class="grid-18">
+    {{lib.submit_button('Save Changes')}}
+    </div>
+  </form>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/ForgeUserStats/forgeuserstats/controllers/userstats.py
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/controllers/userstats.py b/ForgeUserStats/forgeuserstats/controllers/userstats.py
index b84eb0f..3944557 100644
--- a/ForgeUserStats/forgeuserstats/controllers/userstats.py
+++ b/ForgeUserStats/forgeuserstats/controllers/userstats.py
@@ -5,6 +5,7 @@ from allura.controllers import BaseController
 import allura.model as M
 from allura.lib.graphics.graphic_methods import create_histogram, create_progress_bar
 from forgeuserstats.model.stats import UserStats
+from pylons import c
 
 class ForgeUserStatsController(BaseController):
 
@@ -33,6 +34,8 @@ class ForgeUserStatsController(BaseController):
         if not self.user: 
             return dict(user=None)
         stats = self.user.stats
+        if (not stats.visible) and (c.user != self.user):
+            return dict(user=self.user)
 
         ret_dict = _getDataForCategory(None, stats)
         ret_dict['user'] = self.user
@@ -133,6 +136,8 @@ class ForgeUserStatsCatController(BaseController):
         if not self.user:
             return dict(user=None)
         stats = self.user.stats
+        if (not stats.visible) and (c.user != self.user):
+            return dict(user=self.user)
         
         cat_id = None
         if self.category: 
@@ -156,6 +161,8 @@ class ForgeUserStatsMetricController(BaseController):
         if not self.user:
             return dict(user=None)
         stats = self.user.stats
+        if (not stats.visible) and (c.user != self.user):
+            return dict(user=self.user)
         
         commits = stats.getCommitsByCategory()
         return dict(user = self.user,
@@ -166,6 +173,8 @@ class ForgeUserStatsMetricController(BaseController):
     def artifacts(self, **kw):
         if not self.user:
             return dict(user=None)
+        if (not stats.visible) and (c.user != self.user):
+            return dict(user=self.user)
 
         stats = self.user.stats       
         artifacts = stats.getArtifactsByCategory(detailed=True)
@@ -176,6 +185,8 @@ class ForgeUserStatsMetricController(BaseController):
     def tickets(self, **kw):
         if not self.user: 
             return dict(user=None)
+        if (not stats.visible) and (c.user != self.user):
+            return dict(user=self.user)
 
         artifacts = self.user.stats.getTicketsByCategory()
         return dict(user = self.user, data = artifacts) 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/ForgeUserStats/forgeuserstats/templates/artifacts.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/artifacts.html b/ForgeUserStats/forgeuserstats/templates/artifacts.html
index 9492628..d6a01d6 100644
--- a/ForgeUserStats/forgeuserstats/templates/artifacts.html
+++ b/ForgeUserStats/forgeuserstats/templates/artifacts.html
@@ -9,7 +9,7 @@
 
 {% block content %}
 
-  {% if user %}
+  {% if user and (user.stats.visible or (c.user == user)) %}
     <div class="grid-20">
       <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
     </div>
@@ -47,6 +47,19 @@
       </table>
     </div>
     {% endif %}
+  {% else %}
+    {% if not user.stats.visible %}
+      <h2>Statistics not available</h2>
+      <div class="grid-20"> 
+        This user has set his or her preferences so that personal statistics are not visible
+        to other users of the forge.
+      </div>
+    {% else %}
+      <h2>Invalid user</h2>
+      <div class="grid-20"> 
+        You are looking for personal statistics of a user which doesn't exist on this forge. Check your url.
+      </div>
+    {% endif %}
   {% endif %}
 
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/ForgeUserStats/forgeuserstats/templates/commits.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/commits.html b/ForgeUserStats/forgeuserstats/templates/commits.html
index 12f9712..cec0ab7 100644
--- a/ForgeUserStats/forgeuserstats/templates/commits.html
+++ b/ForgeUserStats/forgeuserstats/templates/commits.html
@@ -9,7 +9,7 @@
 
 {% block content %}
 
-  {% if user %}
+  {% if user and (user.stats.visible or (c.user == user)) %}
     <div class="grid-20">
       <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
     </div>
@@ -37,5 +37,18 @@
       </table>
     </div>
     {% endif %}
+  {% else %}
+    {% if not user.stats.visible %}
+      <h2>Statistics not available</h2>
+      <div class="grid-20"> 
+        This user has set his or her preferences so that personal statistics are not visible
+        to other users of the forge.
+      </div>
+    {% else %}
+      <h2>Invalid user</h2>
+      <div class="grid-20"> 
+        You are looking for personal statistics of a user which doesn't exist on this forge. Check your url.
+      </div>
+    {% endif %}
   {% endif %}
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/ForgeUserStats/forgeuserstats/templates/index.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/index.html b/ForgeUserStats/forgeuserstats/templates/index.html
index 37cd5da..4a8f504 100644
--- a/ForgeUserStats/forgeuserstats/templates/index.html
+++ b/ForgeUserStats/forgeuserstats/templates/index.html
@@ -11,7 +11,7 @@
 {% endblock %}
 
 {% block content %}
-  {% if user %}
+  {% if user and (user.stats.visible or (c.user == user)) %}
 
     {% if category %}
        <ul>
@@ -418,7 +418,18 @@
       </p>
     {% endif %}
   {% else %}
-    Invalid user!
+    {% if not user.stats.visible %}
+      <h2>Statistics not available</h2>
+      <div class="grid-20"> 
+        This user has set his or her preferences so that personal statistics are not visible
+        to other users of the forge.
+      </div>
+    {% else %}
+      <h2>Invalid user</h2>
+      <div class="grid-20"> 
+        You are looking for personal statistics of a user which doesn't exist on this forge. Check your url.
+      </div>
+    {% endif %}
   {% endif %}
 
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fa9f88ba/ForgeUserStats/forgeuserstats/templates/tickets.html
----------------------------------------------------------------------
diff --git a/ForgeUserStats/forgeuserstats/templates/tickets.html b/ForgeUserStats/forgeuserstats/templates/tickets.html
index 4604c16..6ee66d4 100644
--- a/ForgeUserStats/forgeuserstats/templates/tickets.html
+++ b/ForgeUserStats/forgeuserstats/templates/tickets.html
@@ -9,7 +9,7 @@
 
 {% block content %}
 
-  {% if user %}
+  {% if user and (user.stats.visible or (c.user == user)) %}
     <div class="grid-20">
       <ul><li><a href="/userstats/{{user.username}}">Go back to general statistics</a></li></ul>
     </div>
@@ -47,5 +47,18 @@
       </table>
     </div>
     {% endif %}
+  {% else %}
+    {% if not user.stats.visible %}
+      <h2>Statistics not available</h2>
+      <div class="grid-20"> 
+        This user has set his or her preferences so that personal statistics are not visible
+        to other users of the forge.
+      </div>
+    {% else %}
+      <h2>Invalid user</h2>
+      <div class="grid-20"> 
+        You are looking for personal statistics of a user which doesn't exist on this forge. Check your url.
+      </div>
+    {% endif %}
   {% endif %}
 {% endblock %}