src/templates/people/subscribed.html \
src/templates/people/unsubscribe.html \
src/templates/people/unsubscribed.html \
- src/templates/people/user-edit.html \
- src/templates/people/users.html
+ src/templates/people/user-edit.html
templates_peopledir = $(templatesdir)/people
templates_people_modules_DATA = \
src/templates/people/modules/accounts-list.html \
- src/templates/people/modules/accounts-new.html \
src/templates/people/modules/agent.html \
src/templates/people/modules/cdr.html \
src/templates/people/modules/channels.html \
templates_staticdir = $(templatesdir)/static
templates_users_DATA = \
+ src/templates/users/index.html \
src/templates/users/show.html
templates_usersdir = $(templatesdir)/users
+templates_users_modules_DATA = \
+ src/templates/users/modules/list.html
+
+templates_users_modulesdir = $(templates_usersdir)/modules
+
templates_wiki_DATA = \
src/templates/wiki/404.html \
src/templates/wiki/base.html \
def _format_date(t):
return t.strftime("%Y%m%d%H%M%SZ")
+ def get_recently_registered(self, limit=None):
+ # Check the last two weeks
+ t = datetime.datetime.utcnow() - datetime.timedelta(days=14)
+
+ # Fetch all accounts created after t
+ accounts = self.get_created_after(t)
+
+ # Order by creation date and put latest first
+ accounts.sort(key=lambda a: a.created_at, reverse=True)
+
+ # Cap at the limit
+ if accounts and limit:
+ accounts = accounts[:limit]
+
+ return accounts
+
def get_created_after(self, ts):
return self._search("(&(objectClass=person)(createTimestamp>=%s))" % self._format_date(ts))
return self._count("(&(objectClass=person)(createTimestamp>=%s))" % self._format_date(ts))
def search(self, query):
- accounts = self._search("(&(objectClass=person)(|(cn=*%s*)(uid=*%s*)(displayName=*%s*)(mail=*%s*)))" \
- % (query, query, query, query))
+ # Try finding an exact match
+ account = self._search_one(
+ "(&"
+ "(objectClass=person)"
+ "(|"
+ "(uid=%s)"
+ "(mail=%s)"
+ "(mailAlternateAddress=%s)"
+ ")"
+ ")" % (query, query, query))
+ if account:
+ return [account]
+
+ # Otherwise search for a substring match
+ accounts = self._search(
+ "(&"
+ "(objectClass=person)"
+ "(|"
+ "(cn=*%s*)"
+ "(uid=*%s*)"
+ "(displayName=*%s*)"
+ "(mail=*%s*)"
+ ")"
+ ")" % (query, query, query, query))
return sorted(accounts)
+++ /dev/null
-{% if accounts %}
- <h3>{{ _("Recently Created Accounts") }}</h3>
-
- {% module AccountsList(accounts) %}
-{% end %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ _("Users") }}{% end block %}
-
-{% block content %}
- <div class="row justify-content-center">
- <div class="col col-md-8">
- <h1>{{ _("Users") }}</h1>
-
- {% module NewAccounts() %}
- </div>
- </div>
-{% end block %}
--- /dev/null
+{% extends "../base.html" %}
+
+{% block title %}{{ _("Users") }}{% end block %}
+
+{% block container %}
+ <section class="hero is-dark">
+ <div class="hero-body">
+ <div class="container">
+ <nav class="breadcrumb is-medium" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/">
+ {{ _("Home") }}
+ </a>
+ </li>
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Users") }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <h1 class="title is-1">{{ _("Users") }}</h1>
+ </div>
+ </div>
+ </section>
+
+ {# Search #}
+
+ <section class="section">
+ <div class="container">
+ <div class="columns is-centered">
+ <div class="column is-one-third">
+ <form action="" method="GET">
+ <div class="field">
+ <div class="control has-icons-left">
+ <input class="input is-medium" type="text"
+ name="q" {% if q %}value="{{ q }}"{% end %}
+ placeholder="{{ _("Search...") }}">
+ <span class="icon is-small is-left">
+ <i class="fas fa-search"></i>
+ </span>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ {# Search Results #}
+
+ {% if q %}
+ <section class="section">
+ <div class="container">
+ <h4 class="title is-4">{{ _("Search Results for '%s'") % q }}</h4>
+
+ {% if results %}
+ {% module UsersList(results) %}
+ {% else %}
+ <div class="notification has-text-centered">
+ {{ _("Nothing found for '%s'") % q }}
+ </div>
+ {% end %}
+ </div>
+ </section>
+
+ {% else %}
+ {# Recently Joined #}
+
+ {% set recently_registered = backend.accounts.get_recently_registered(limit=4) %}
+ {% if recently_registered %}
+ <section class="section">
+ <div class="container">
+ <h4 class="title is-4">{{ _("Recently Joined") }}</h4>
+
+ {% module UsersList(recently_registered, show_created_at=True) %}
+ </div>
+ </section>
+ {% end %}
+ {% end %}
+{% end block %}
--- /dev/null
+<div class="block">
+ <div class="columns is-multiline">
+ {% for account in accounts %}
+ <div class="column is-half">
+ <div class="box">
+ <div class="columns is-mobile">
+ <div class="column is-narrow">
+ <figure class="image is-64x64">
+ <img class="is-rounded" src="{{ account.avatar_url(64) }}">
+ </figure>
+ </div>
+
+ <div class="column">
+ <h5 class="title is-5">
+ <a href="/users/{{ account.uid }}">{{ account }}</a>
+ </h5>
+
+ {% if show_created_at %}
+ <p>
+ {{ _("Joined %s") % locale.format_date(account.created_at, shorter=True) }}
+ </p>
+ {% end %}
+ </div>
+ </div>
+ </div>
+ </div>
+ {% end %}
+ </div>
+</div>
"CDR" : people.CDRModule,
"Channels" : people.ChannelsModule,
"MOS" : people.MOSModule,
- "NewAccounts" : people.NewAccountsModule,
"Password" : people.PasswordModule,
"Registrations" : people.RegistrationsModule,
"FireinfoDeviceAndGroupsTable"
: fireinfo.DeviceAndGroupsTableModule,
+ # Users
+ "UsersList" : users.ListModule,
+
# Wiki
"WikiDiff" : wiki.WikiDiffModule,
"WikiNavbar" : wiki.WikiNavbarModule,
(r"/donation", tornado.web.RedirectHandler, { "url" : "/donate" }),
# Users
+ (r"/users", users.IndexHandler),
(r"/users/([\w]+)", users.ShowHandler),
# RSS feed
(r"/groups/([a-z_][a-z0-9_-]{0,31})", people.GroupHandler),
(r"/register", auth.RegisterHandler),
(r"/search", people.SearchHandler),
- (r"/users", people.UsersHandler),
(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/sip.html", account=account)
-class UsersHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self):
- # Only staff can see other users
- if not self.current_user.is_staff():
- raise tornado.web.HTTPError(403)
-
- self.render("people/users.html")
-
-
class UserEditHandler(auth.CacheMixin, base.BaseHandler):
@tornado.web.authenticated
def get(self, uid):
self.redirect("%s?%s" % (params.get("return_sso_url"), qs))
-class NewAccountsModule(ui_modules.UIModule):
- def render(self, days=14):
- t = datetime.datetime.utcnow() - datetime.timedelta(days=days)
-
- # Fetch all accounts created after t
- accounts = self.backend.accounts.get_created_after(t)
-
- accounts.sort(key=lambda a: a.created_at, reverse=True)
-
- return self.render_string("people/modules/accounts-new.html",
- accounts=accounts, t=t)
-
-
class AccountsListModule(ui_modules.UIModule):
def render(self, accounts=None):
if accounts is None:
from . import auth
from . import base
+from . import ui_modules
+
+class IndexHandler(auth.CacheMixin, base.BaseHandler):
+ @tornado.web.authenticated
+ def get(self):
+ results = None
+
+ # Query Term
+ q = self.get_argument("q", None)
+
+ # Peform search
+ if q:
+ results = self.backend.accounts.search(q)
+
+ self.render("users/index.html", q=q, results=results)
+
class ShowHandler(auth.CacheMixin, base.BaseHandler):
@tornado.web.authenticated
raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
self.render("users/show.html", account=account)
+
+
+class ListModule(ui_modules.UIModule):
+ def render(self, accounts, show_created_at=False):
+ return self.render_string("users/modules/list.html", accounts=accounts,
+ show_created_at=show_created_at)