Yuki Takei 8 лет назад
Родитель
Сommit
5f21d69149

+ 39 - 0
lib/models/external-account.js

@@ -1,5 +1,6 @@
 const debug = require('debug')('crowi:models:external-account');
 const mongoose = require('mongoose');
+const mongoosePaginate = require('mongoose-paginate');
 const uniqueValidator = require('mongoose-unique-validator');
 const ObjectId = mongoose.Schema.Types.ObjectId;
 
@@ -15,6 +16,7 @@ const schema = new mongoose.Schema({
 // compound index
 schema.index({ providerType: 1, accountId: 1 });
 // apply plugins
+schema.plugin(mongoosePaginate);
 schema.plugin(uniqueValidator);
 
 /**
@@ -24,6 +26,17 @@ schema.plugin(uniqueValidator);
  */
 class ExternalAccount {
 
+  /**
+   * limit items num for pagination
+   *
+   * @readonly
+   * @static
+   * @memberof ExternalAccount
+   */
+  static get DEFAULT_LIMIT() {
+    return 50;
+  }
+
   static set crowi(crowi) {
     this._crowi = crowi;
   }
@@ -88,6 +101,32 @@ class ExternalAccount {
 
   }
 
+  /**
+   * find all entities with pagination
+   *
+   * @see https://github.com/edwardhotchkiss/mongoose-paginate
+   *
+   * @static
+   * @param {any} opts mongoose-paginate options object
+   * @returns {Promise<any>} mongoose-paginate result object
+   * @memberof ExternalAccount
+   */
+  static findAllWithPagination(opts) {
+    const query = {};
+    const options = Object.assign({ populate: 'user' }, opts);
+    if (options.sort == null) {
+      options.sort = {accountId: 1, createdAt: 1};
+    }
+    if (options.limit == null) {
+      options.limit = ExternalAccount.DEFAULT_LIMIT;
+    }
+
+    return this.paginate(query, options)
+      .catch((err) => {
+        debug('Error on pagination:', err);
+      });
+  }
+
 }
 
 /**

+ 16 - 0
lib/routes/admin.js

@@ -5,6 +5,7 @@ module.exports = function(crowi, app) {
     , models = crowi.models
     , Page = models.Page
     , User = models.User
+    , ExternalAccount = models.ExternalAccount
     , Config = models.Config
     , PluginUtils = require('../plugins/plugin-utils')
     , pluginUtils = new PluginUtils()
@@ -471,6 +472,21 @@ module.exports = function(crowi, app) {
     });
   }
 
+  actions.externalAccount = {};
+  actions.externalAccount.index = function(req, res) {
+    const page = parseInt(req.query.page) || 1;
+
+    ExternalAccount.findAllWithPagination({page})
+      .then((result) => {
+        const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
+
+        return res.render('admin/external-accounts', {
+          accounts: result.docs,
+          pager: pager
+        })
+      });
+  };
+
   actions.api = {};
   actions.api.appSetting = function(req, res) {
     var form = req.form.settingForm;

+ 2 - 0
lib/routes/index.js

@@ -102,6 +102,8 @@ module.exports = function(crowi, app) {
   // new route patterns from here:
   app.post('/_api/admin/users.resetPassword'  , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.resetPassword);
 
+  app.get('/admin/external-accounts'    , loginRequired(crowi, app) , middleware.adminRequired() , admin.externalAccount.index);
+
   app.get('/me'                       , loginRequired(crowi, app) , me.index);
   app.get('/me/password'              , loginRequired(crowi, app) , me.password);
   app.get('/me/apiToken'              , loginRequired(crowi, app) , me.apiToken);

+ 122 - 0
lib/views/admin/external-accounts.html

@@ -0,0 +1,122 @@
+{% extends '../layout/admin.html' %}
+
+{% block html_title %}外部アカウント管理 · {% endblock %}
+
+{% block content_head %}
+<div class="header-wrap">
+  <header id="page-header">
+    <h1 class="title" id="">外部アカウント管理</h1>
+  </header>
+</div>
+{% endblock %}
+
+{% block content_main %}
+<div class="content-main">
+  {% set smessage = req.flash('successMessage') %}
+  {% if smessage.length %}
+  <div class="alert alert-success">
+    {{ smessage }}
+  </div>
+  {% endif %}
+
+  {% set emessage = req.flash('errorMessage') %}
+  {% if emessage.length %}
+  <div class="alert alert-danger">
+    {{ emessage }}
+  </div>
+  {% endif %}
+
+  <div class="row">
+    <div class="col-md-3">
+      {% include './widget/menu.html' with {current: 'external-account'} %}
+    </div>
+
+    <div class="col-md-9">
+      <p>
+        <a class="btn btn-default" href="/admin/users">
+          <i class="fa fa-arrow-left" aria-hidden="true"></i>
+          ユーザー管理に戻る
+        </a>
+      </p>
+
+      <h2>外部アカウント一覧</h2>
+
+      <table class="table table-hover table-striped table-bordered table-user-list">
+        <thead>
+          <tr>
+            <th width="120px">Authentication Provider</th>
+            <th><code>accountId</code></th>
+            <th>関連付けられているユーザーの <code>username</code></th>
+            <th>
+              パスワード設定
+              <a class="btn btn-sm btn-link"
+                  data-toggle="popover" data-placement="bottom"
+                  data-trigger="focus" tabindex="0" role="button" {# dismiss settings #}
+                  data-animation="false" data-html="true"
+                  data-content="<small>関連付けられているユーザーがパスワードを設定しているかどうかを表示します。<br>未設定のアカウントを削除するとログインできなくなるので注意してください。</small>">
+                <i class="fa fa-info-circle" aria-hidden="true"></i>
+              </a>
+            </th>
+            <th width="100px">作成日</th>
+            <th width="90px">操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          {% for account in accounts %}
+          <tr>
+            <td>{{ account.providerType }}</td>
+            <td>
+              <strong>{{ account.accountId }}</strong>
+            </td>
+            <td>
+              <strong>{{ account.user.username }}</strong>
+            </td>
+            <td>
+              {% if account.user.password != null %}
+              <span class="label label-info">
+                設定済み
+              </span>
+              {% else %}
+              <span class="label label-warning">
+                未設定
+              </span>
+              {% endif %}
+            </td>
+            <td>{{ account.createdAt|date('Y-m-d', account.createdAt.getTimezoneOffset()) }}</td>
+            <td>
+              <div class="btn-group admin-user-menu">
+
+                <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
+                  編集
+                  <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu" role="menu">
+                  <li class="dropdown-header">編集メニュー</li>
+                  <li class="dropdown-button">
+                    <form action="/admin/external-accounts/{{ account.accountId }}/remove" method="post">
+                      <input type="hidden" name="_csrf" value="{{ csrf() }}">
+                      <button type="submit" class="btn btn-block btn-danger">削除する</button>
+                    </form>
+                  </li>
+                </ul>{# end of .dropdown-menu #}
+
+
+
+              </div>{# end of .btn-group #}
+            </td>
+          </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+
+      {% include '../widget/pager.html' with {path: "/admin/external-accounts", pager: pager} %}
+
+    </div>
+  </div>
+</div>
+{% endblock content_main %}
+
+{% block content_footer %}
+{% endblock content_footer %}
+
+

+ 5 - 1
lib/views/admin/users.html

@@ -33,7 +33,11 @@
 
     <div class="col-md-9">
       <p>
-        <button  data-toggle="collapse" class="btn btn-default" href="#inviteUserForm">新規ユーザーの招待</button>
+        <button data-toggle="collapse" class="btn btn-default" href="#inviteUserForm">新規ユーザーの招待</button>
+        <a class="btn btn-default" href="/admin/external-accounts">
+          <i class="fa fa-user-plus" aria-hidden="true"></i>
+          外部アカウントの管理
+        </a>
       </p>
       <form role="form" action="/admin/user/invite" method="post">
         <div id="inviteUserForm" class="collapse">