src/web/nopaste.py \
src/web/people.py \
src/web/ui_modules.py \
+ src/web/users.py \
src/web/wiki.py
webdir = $(backenddir)/web
src/templates/people/subscribed.html \
src/templates/people/unsubscribe.html \
src/templates/people/unsubscribed.html \
- src/templates/people/user.html \
src/templates/people/user-edit.html \
src/templates/people/users.html
templates_staticdir = $(templatesdir)/static
+templates_users_DATA = \
+ src/templates/users/show.html
+
+templates_usersdir = $(templatesdir)/users
+
templates_wiki_DATA = \
src/templates/wiki/404.html \
src/templates/wiki/base.html \
def has_sip(self):
return "sipUser" in self.classes or "sipRoutingObject" in self.classes
+ def is_lwl(self):
+ return self.is_member_of_group("lwl-staff")
+
def can_be_managed_by(self, account):
"""
Returns True if account is allowed to manage this account
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }}{% end block %}
-
-{% block container %}
- {% import phonenumbers %}
-
- <div class="header">
- <div class="container">
- <div class="row align-items-center justify-content-center">
- <div class="col-4 col-lg-2 text-center">
- <img class="img-fluid rounded-circle my-5"
- src="{{ account.avatar_url(512) }}" alt="{{ account }}" />
- </div>
-
- <div class="col-12 col-lg-10">
- <h2>{{ account }}</h2>
-
- {% if account.description %}
- <div>
- {% module Markdown(account.description) %}
- </div>
- {% end %}
-
- <div class="btn-toolbar mb-3">
- <a class="btn btn-secondary btn-sm mr-2" href="https://community.ipfire.org/u/{{ account.uid }}">
- <i class="fas fa-users"></i> {{ _("Community Profile") }}
- </a>
-
- {% if account.has_shell() %}
- <a class="btn btn-secondary btn-sm" href="/~{{ account.uid }}/">
- <span class="fas fa-home mr-2"></span> {{ _("Home Directory") }}
- </a>
- {% end %}
-
- {% if account.can_be_managed_by(current_user) %}
- {% if account.has_sip() %}
- <a class="btn btn-secondary btn-sm ml-2" href="/users/{{ account.uid }}/calls">
- <span class="fas fa-phone mr-2"></span> {{ _("Calls") }}
- </a>
- {% end %}
-
- <a class="btn btn-link text-warning btn-sm" href="/users/{{ account.uid }}/edit">
- {{ _("Edit") }}
- </a>
-
- <a class="btn btn-link text-muted btn-sm" href="/users/{{ account.uid }}/passwd">
- {{ _("Change Password") }}
- </a>
- {% end %}
- </div>
- </div>
- </div>
- </div>
- </div>
-
- {% if current_user == account or current_user.is_staff() %}
- <div class="container">
- <section>
- {% if account == current_user %}
- {# Suggest adding a description #}
- {% if not current_user.description %}
- <div class="alert alert-info">
- <h6 class="alert-heading">{{ _("Tell Us About Yourself" ) }}</h6>
-
- <p class="mb-0">
- {{ _("Add a couple of lines about yourself to your profile so that others get to know you better") }}
-
- <a class="alert-link" href="/users/{{ account.uid }}/edit#description">{{ _("Edit Profile") }}</a>
- </p>
- </div>
-
- {# Suggest uploading an avatar if this user does not have one #}
- {% elif not current_user.has_avatar() %}
- <div class="alert alert-info">
- <h6 class="alert-heading">{{ _("Upload An Avatar" ) }}</h6>
-
- <p class="mb-0">
- {{ _("A picture says more than a thousand words") }}
-
- <a class="alert-link" href="/users/{{ account.uid }}/edit#avatar">{{ _("Upload Avatar") }}</a>
- </p>
- </div>
- {% end %}
- {% end %}
-
- <div class="row">
- <div class="col-12 col-lg-6">
- {% if current_user.is_staff() %}
- <h6>{{ _("Email") }}</h6>
-
- <ul class="list-unstyled">
- <li>
- <a href="mailto:{{ account.email }}">
- {{ account.email }}
- </a>
- </li>
- </ul>
- {% end %}
-
- {% if account.address %}
- <h6>{{ _("Address") }}</h6>
-
- <address>
- {{ account.name }}<br>
- {% for line in account.address %}
- {{ line }}<br>
- {% end %}
- </address>
- {% end %}
-
- {% if account.phone_number or account.fax_number %}
- <h6>{{ _("Phone Numbers") }}</h6>
-
- <ul class="list-unstyled">
- {% if account.has_sip() %}
- <li>
- {% if account.sip_channels %}
- <i class="fas fa-phone-volume text-warning fa-fw"></i>
- {% elif account.sip_registrations %}
- <i class="fas fa-phone-square text-success fa-fw"></i>
- {% elif account.uses_sip_forwarding() %}
- <i class="fas fa-phone-square text-warning fa-fw"></i>
- {% else %}
- <i class="fas fa-phone-square text-danger fa-fw"></i>
- {% end %}
-
- <a href="sip:{{ account.sip_url }}">{{ account.sip_id }}</a>
- </li>
- {% end %}
-
- {% if account.phone_number %}
- <li>
- <i class="fas fa-phone fa-fw"></i>
-
- <a href="tel:{{ format_phone_number_to_e164(account.phone_number) }}">
- {{ format_phone_number(account.phone_number) }}
- </a>
- </li>
- {% end %}
-
- {% if account.fax_number %}
- <li>
- <i class="fas fa-fax fa-fw"></i>
-
- <a href="fax:{{ format_phone_number_to_e164(account.fax_number) }}">
- {{ format_phone_number(account.fax_number) }}
- </a>
- </li>
- {% end %}
- </ul>
- {% end %}
-
- {% if account.phone_numbers %}
- <h6>{{ _("External Phone Numbers") }}</h6>
-
- <ul class="list-unstyled">
- {% for number in account.phone_numbers %}
- <li>
- {% if phonenumbers.number_type(number) == phonenumbers.PhoneNumberType.MOBILE %}
- <i class="fas fa-mobile fa-fw" title="{{ _("Mobile") }}"></i>
- {% else %}
- <i class="fas fa-phone fa-fw"></i>
- {% end %}
-
- <a href="tel:{{ format_phone_number_to_e164(number) }}"
- title="{{ format_phone_number_location(number) }}">{{ format_phone_number(number) }}</a>
- </li>
- {% end %}
- </ul>
- {% end %}
- </div>
-
- {% if current_user.is_staff() %}
- <div class="col-12 col-lg-6">
- {% if account.groups %}
- <h6>{{ _("Groups") }}</h6>
-
- <div class="list-group mb-3">
- {% for g in account.groups %}
- <a class="list-group-item list-group-item-action" href="/groups/{{ g.gid }}">
- <i class="fas fa-users mr-2"></i> {{ g }}
- </a>
- {% end %}
- </div>
- {% end %}
-
- <ul class="list-unstyled small text-muted">
- <li>
- {{ _("Joined %s") % locale.format_date(account.created_at, shorter=True) }}
- </li>
-
- {% if current_user.is_admin() %}
- <li>
- {{ _("Last Modified %s") % locale.format_date(account.modified_at) }}
- </li>
-
- {% if account.last_successful_authentication %}
- <li>
- {{ _("Last successful authentication: %s") % locale.format_date(account.last_successful_authentication) }}
- </li>
- {% end %}
-
- {% if account.failed_login_count %}
- <li class="text-warning">
- {{ _("One unsuccessful authentication attempt.", "%(num)s unsuccessful authentication attempts.", account.failed_login_count) % { "num" : account.failed_login_count } }}
-
- {% if account.last_failed_authentication %}
- {{ _("Last attempt: %s") % locale.format_date(account.last_failed_authentication) }}
- {% end %}
- </li>
- {% end %}
- {% end %}
- </ul>
- </div>
- {% end %}
- </div>
- </section>
- </div>
- {% end %}
-{% end block %}
--- /dev/null
+{% extends "../base.html" %}
+
+{% block title %}{{ account }}{% end block %}
+
+{% block container %}
+ {% import phonenumbers %}
+
+ <section class="hero {% if account.is_lwl() %}is-lwl{% else %}is-dark{% end %}">
+ <div class="hero-body">
+ <div class="container">
+ <div class="columns is-vcentered">
+ <div class="column">
+ <nav class="breadcrumb is-medium" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/">
+ {{ _("Home") }}
+ </a>
+ </li>
+ <li>
+ <a href="/users">
+ {{ _("Users") }}
+ </a>
+ </li>
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ account }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <h1 class="title is-1">{{ account }}</h1>
+ <h6 class="subtitle is-6">{{ account.uid }}</h6>
+
+ {# Description #}
+ {% if account.description %}
+ <div class="block">
+ <div class="content">
+ {% module Markdown(account.description) %}
+ </div>
+ </div>
+ {% end %}
+ </div>
+
+ <div class="column is-narrow is-hidden-mobile">
+ <figure class="image">
+ <img class="is-rounded" src="{{ account.avatar_url(192) }}">
+ </figure>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ <div class="buttons">
+ <a class="button is-light" href="mailto:{{ account.email }}">
+ <span class="icon-text">
+ <span class="icon">
+ <i class="fas fa-envelope"></i>
+ </span>
+ <span>{{ account.email }}</span>
+ </span>
+ </a>
+
+ <a class="button is-light" href="https://community.ipfire.org/u/{{ account.uid }}">
+ <span class="icon-text">
+ <span class="icon">
+ <i class="fas fa-users"></i>
+ </span>
+ <span>{{ _("Community Profile") }}</span>
+ </span>
+ </a>
+
+ {% if account.has_shell() %}
+ <a class="button is-dark" href="/~{{ account.uid }}/">
+ <span class="icon-text">
+ <span class="icon">
+ <i class="fas fa-home"></i>
+ </span>
+ <span>{{ _("Home Directory") }}</span>
+ </span>
+ </a>
+ {% end %}
+
+ {% if account.can_be_managed_by(current_user) %}
+ {% if account.has_sip() %}
+ <a class="button is-success" href="/users/{{ account.uid }}/calls">
+ <span class="icon-text">
+ <span class="icon">
+ <i class="fas fa-phone"></i>
+ </span>
+ <span>{{ _("Calls") }}</span>
+ </span>
+ </a>
+ {% end %}
+
+ <a class="button is-warning" href="/users/{{ account.uid }}/edit">
+ {{ _("Edit") }}
+ </a>
+
+ <a class="button is-light" href="/users/{{ account.uid }}/passwd">
+ {{ _("Change Password") }}
+ </a>
+ {% end %}
+ </div>
+
+ {% if account == current_user %}
+ {# Suggest adding a description #}
+ {% if not current_user.description %}
+ <div class="notification is-info">
+ <strong>{{ _("Tell Us About Yourself!") }}</strong>
+
+ {{ _("Add a couple of lines about yourself to your profile so that others get to know you better") }}
+
+ <a href="/users/{{ account.uid }}/edit#description">{{ _("Edit Profile") }}</a>
+ </div>
+
+ {# Suggest uploading an avatar if this user does not have one #}
+ {% elif not current_user.has_avatar() %}
+ <div class="notification is-info">
+ <strong>{{ _("Upload An Avatar!") }}</strong>
+
+ {{ _("A picture says more than a thousand words") }}
+
+ <a href="/users/{{ account.uid }}/edit#avatar">{{ _("Upload Avatar") }}</a>
+ </div>
+ {% end %}
+ {% end %}
+ </div>
+ </section>
+
+ {% if current_user == account or current_user.is_staff() %}
+ <section class="section">
+ <div class="container">
+ <div class="columns">
+ {# Address #}
+ {% if account.address %}
+ <div class="column">
+ <h6 class="title is-6">{{ _("Address") }}</h6>
+
+ <address>
+ {{ account.name }}<br>
+ {% for line in account.address %}
+ {{ line }}<br>
+ {% end %}
+ </address>
+ </div>
+ {% end %}
+
+ {# Phone Numbers #}
+ {% if account.phone_number or account.fax_number %}
+ <div class="column">
+ <h6 class="title is-6">{{ _("Phone Numbers") }}</h6>
+
+ <ul>
+ {% if account.has_sip() %}
+ <li>
+ {% if account.sip_channels %}
+ <i class="fas fa-phone-volume has-text-warning fa-fw"></i>
+ {% elif account.sip_registrations %}
+ <i class="fas fa-phone-square has-text-success fa-fw"></i>
+ {% elif account.uses_sip_forwarding() %}
+ <i class="fas fa-phone-square has-text-warning fa-fw"></i>
+ {% else %}
+ <i class="fas fa-phone-square has-text-danger fa-fw"></i>
+ {% end %}
+
+ <a href="sip:{{ account.sip_url }}">{{ account.sip_id }}</a>
+ </li>
+ {% end %}
+
+ {% if account.phone_number %}
+ <li>
+ <i class="fas fa-phone fa-fw"></i>
+
+ <a href="tel:{{ format_phone_number_to_e164(account.phone_number) }}">
+ {{ format_phone_number(account.phone_number) }}
+ </a>
+ </li>
+ {% end %}
+
+ {% if account.fax_number %}
+ <li>
+ <i class="fas fa-fax fa-fw"></i>
+
+ <a href="fax:{{ format_phone_number_to_e164(account.fax_number) }}">
+ {{ format_phone_number(account.fax_number) }}
+ </a>
+ </li>
+ {% end %}
+ </ul>
+ </div>
+ {% end %}
+
+ {% if current_user.is_staff() %}
+ {% if account.groups %}
+ <div class="column is-half">
+ <h6 class="title is-6">{{ _("Groups") }}</h6>
+
+ <nav class="panel">
+ {% for g in account.groups %}
+ <a class="panel-block" href="/groups/{{ g.gid }}">
+ <span class="panel-icon">
+ <i class="fas fa-users" aria-hidden="true"></i>
+ </span>
+ {{ g }}
+ </a>
+ {% end %}
+ </nav>
+ </div>
+ {% end %}
+ {% end %}
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="container">
+ <nav class="level">
+ <div class="level-item has-text-centered">
+ <div>
+ <p class="heading">{{ _("Joined") }}</p>
+ <p>
+ {{ locale.format_date(account.created_at, shorter=True) }}
+ </p>
+ </div>
+ </div>
+
+ <div class="level-item has-text-centered">
+ <div>
+ <p class="heading">{{ _("Last Modified") }}</p>
+ <p>
+ {{ locale.format_date(account.modified_at) }}
+ </p>
+ </div>
+ </div>
+
+ {% if account.last_successful_authentication %}
+ <div class="level-item has-text-centered">
+ <div>
+ <p class="heading">{{ _("Last Successful Authentication") }}</p>
+ <p>
+ {{ locale.format_date(account.last_successful_authentication) }}
+ </p>
+ </div>
+ </div>
+ {% end %}
+
+ {% if account.failed_login_count %}
+ <div class="level-item has-text-warning has-text-centered">
+ <div>
+ <p class="heading">{{ _("Failed Authentication Attempts") }}</p>
+ <p>{{ account.failed_login_count }}</p>
+ </div>
+ </div>
+
+ {% if account.last_failed_authentication %}
+ <div class="level-item has-text-warning has-text-centered">
+ <div>
+ <p class="heading">{{ _("Last Failed Authentication Attempt") }}</p>
+ <p>{{ locale.format_date(account.last_failed_authentication) }}</p>
+ </div>
+ </div>
+ {% end %}
+ {% end %}
+ </nav>
+ </div>
+ </section>
+ {% end %}
+{% end block %}
from . import nopaste
from . import people
from . import ui_modules
+from . import users
from . import wiki
class Application(tornado.web.Application):
(r"/donate/error", donate.ErrorHandler),
(r"/donation", tornado.web.RedirectHandler, { "url" : "/donate" }),
+ # Users
+ (r"/users/([\w]+)", users.ShowHandler),
+
# RSS feed
(r"/news.rss", tornado.web.RedirectHandler, { "url" : "https://blog.ipfire.org/feed.xml" }),
(r"/register", auth.RegisterHandler),
(r"/search", people.SearchHandler),
(r"/users", people.UsersHandler),
- (r"/users/([a-z_][a-z0-9_-]{0,31})", people.UserHandler),
(r"/users/([a-z_][a-z0-9_-]{0,31})\.jpg", people.AvatarHandler),
(r"/users/([a-z_][a-z0-9_-]{0,31})/calls/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", people.CallHandler),
(r"/users/([a-z_][a-z0-9_-]{0,31})/calls(?:/(\d{4}-\d{2}-\d{2}))?", people.CallsHandler),
self.render("people/users.html")
-class UserHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- self.render("people/user.html", account=account)
-
-
class UserEditHandler(auth.CacheMixin, base.BaseHandler):
@tornado.web.authenticated
def get(self, uid):
--- /dev/null
+#!/usr/bin/python
+
+import tornado.web
+
+from . import auth
+from . import base
+
+class ShowHandler(auth.CacheMixin, base.BaseHandler):
+ @tornado.web.authenticated
+ def get(self, uid):
+ account = self.backend.accounts.get_by_uid(uid)
+ if not account:
+ raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
+
+ self.render("users/show.html", account=account)