Sotaro KARASAWA 10 лет назад
Родитель
Сommit
4757c71f75

+ 51 - 30
lib/models/page.js

@@ -18,34 +18,6 @@ module.exports = function(crowi) {
     return false;
   }
 
-  function populatePageData(pageData, revisionId) {
-    var Page = crowi.model('Page');
-
-    pageData.latestRevision = pageData.revision;
-    if (revisionId) {
-      pageData.revision = revisionId;
-    }
-    pageData.likerCount = pageData.liker.length || 0;
-    pageData.seenUsersCount = pageData.seenUsers.length || 0;
-
-    return new Promise(function(resolve, reject) {
-      pageData.populate([
-        {path: 'creator', model: 'User', select: USER_PUBLIC_FIELDS},
-        {path: 'revision', model: 'Revision'},
-        //{path: 'liker', options: { limit: 11 }},
-        //{path: 'seenUsers', options: { limit: 11 }},
-      ], function (err, pageData) {
-        Page.populate(pageData, {path: 'revision.author', model: 'User', select: USER_PUBLIC_FIELDS}, function(err, data) {
-          if (err) {
-            return reject(err);
-          }
-
-          return resolve(data);
-        });
-      });
-    });
-  }
-
   pageSchema = new mongoose.Schema({
     path: { type: String, required: true, index: true },
     revision: { type: ObjectId, ref: 'Revision' },
@@ -203,6 +175,34 @@ module.exports = function(crowi) {
     });
   };
 
+  pageSchema.statics.populatePageData = function(pageData, revisionId) {
+    var Page = crowi.model('Page');
+
+    pageData.latestRevision = pageData.revision;
+    if (revisionId) {
+      pageData.revision = revisionId;
+    }
+    pageData.likerCount = pageData.liker.length || 0;
+    pageData.seenUsersCount = pageData.seenUsers.length || 0;
+
+    return new Promise(function(resolve, reject) {
+      pageData.populate([
+        {path: 'creator', model: 'User', select: USER_PUBLIC_FIELDS},
+        {path: 'revision', model: 'Revision'},
+        //{path: 'liker', options: { limit: 11 }},
+        //{path: 'seenUsers', options: { limit: 11 }},
+      ], function (err, pageData) {
+        Page.populate(pageData, {path: 'revision.author', model: 'User', select: USER_PUBLIC_FIELDS}, function(err, data) {
+          if (err) {
+            return reject(err);
+          }
+
+          return resolve(data);
+        });
+      });
+    });
+  };
+
   pageSchema.statics.updateCommentCount = function (page, num)
   {
     var self = this;
@@ -255,6 +255,7 @@ module.exports = function(crowi) {
       /^\/_api\/.*/,
       /^\/\-\/.*/,
       /^\/_r\/.*/,
+      /^\/user\/[^\/]+\/(bookmarks|comments|activities|pages|recent-create|recent-edit)/, // reserved
       /.+\/edit$/,
       /^\/(installer|register|login|logout|admin|me|files|trash|paste|comments).+/,
     ];
@@ -297,7 +298,7 @@ module.exports = function(crowi) {
           return reject(err);
         }
 
-        return populatePageData(pageData, null).then(resolve);
+        return Page.populatePageData(pageData, null).then(resolve);
       });
     });
   };
@@ -343,12 +344,31 @@ module.exports = function(crowi) {
           return reject(new Error('Page is not granted for the user')); //PAGE_GRANT_ERROR, null);
         }
 
-        populatePageData(pageData, revisionId || null).then(resolve).catch(reject);
+        self.populatePageData(pageData, revisionId || null).then(resolve).catch(reject);
       });
     });
   };
 
   pageSchema.statics.findListByPageIds = function(ids, option) {
+    var Page = this;
+    var limit = option.limit || 50;
+    var offset = option.skip || 0;
+
+    return new Promise(function(resolve, reject) {
+      Page
+        .find({ _id: { $in: ids }, grant: GRANT_PUBLIC })
+        //.sort({createdAt: -1}) // TODO optionize
+        .skip(offset)
+        .limit(limit)
+        .populate('revision')
+        .exec(function(err, pages) {
+          if (err) {
+            return reject(err);
+          }
+
+          return resolve(pages);
+        });
+    });
   };
 
   pageSchema.statics.findListByCreator = function(user, option) {
@@ -362,6 +382,7 @@ module.exports = function(crowi) {
         .sort({createdAt: -1})
         .skip(offset)
         .limit(limit)
+        .populate('revision')
         .exec(function(err, pages) {
           if (err) {
             return reject(err);

+ 25 - 7
lib/models/user.js

@@ -23,13 +23,14 @@ module.exports = function(crowi) {
     image: String,
     googleId: String,
     name: { type: String },
-    username: { type: String },
-    email: { type: String, required: true },
+    username: { type: String, index: true },
+    email: { type: String, required: true, index: true  },
+    introduction: { type: String },
     password: String,
     apiToken: String,
-    status: { type: Number, required: true, default: STATUS_ACTIVE },
+    status: { type: Number, required: true, default: STATUS_ACTIVE, index: true  },
     createdAt: { type: Date, default: Date.now },
-    admin: { type: Boolean, default: 0 }
+    admin: { type: Boolean, default: 0, index: true  }
   });
   userSchema.plugin(mongoosePaginate);
 
@@ -285,9 +286,16 @@ module.exports = function(crowi) {
     }, { sortBy : sort });
   };
 
-  userSchema.statics.findUserByUsername = function(username, callback) {
-    this.findOne({username: username}, function (err, userData) {
-      callback(err, userData);
+  userSchema.statics.findUserByUsername = function(username) {
+    var User = this;
+    return new Promise(function(resolve, reject) {
+      User.findOne({username: username}, function (err, userData) {
+        if (err) {
+          return reject(err);
+        }
+
+        return resolve(userData);
+      });
     });
   };
 
@@ -523,12 +531,22 @@ module.exports = function(crowi) {
     });
   };
 
+
   userSchema.statics.createUserPictureFilePath = function(user, name) {
     var ext = '.' + name.match(/(.*)(?:\.([^.]+$))/)[2];
 
     return 'user/' + user._id + ext;
   };
 
+  userSchema.statics.getUsernameByPath = function(path) {
+    var username = null;
+    if (m = path.match(/^\/user\/([^\/]+)\/?/)) {
+      username = m[1];
+    }
+
+    return username;
+  };
+
 
   userSchema.statics.STATUS_REGISTERED = STATUS_REGISTERED;
   userSchema.statics.STATUS_ACTIVE = STATUS_ACTIVE;

+ 25 - 17
lib/routes/page.js

@@ -114,35 +114,43 @@ module.exports = function(crowi, app) {
       author: pageData.revision.author || false,
     };
     var userPage = isUserPage(pageData.path);
+    var userData = null;
 
     Revision.findRevisionList(pageData.path, {})
     .then(function(tree) {
       renderVars.tree = tree;
 
+      return Promise.resolve();
+    }).then(function() {
       if (userPage) {
-        return Bookmark.findByUser(req.user, {limit: 10});
-      } else {
-        return Promise.resolve();
-      }
-    }).then(function(bookmarkList) {
-      if (bookmarkList) {
-        renderVars.bookmarkList = bookmarkList;
-      }
+        return User.findUserByUsername(User.getUsernameByPath(pageData.path))
+        .then(function(data) {
+          if (data === null) {
+            throw new Error('The user not found.');
+          }
+          userData = data;
+          renderVars.pageUser = userData;
 
-      if (userPage) {
-        return Page.findListByCreator(req.user, {limit: 10});
+          return Bookmark.findByUser(userData, {limit: 10}).then(function(bookmarkList) {
+            return Page.findListByPageIds(Object.keys(bookmarkList).map(function(e) { return bookmarkList[e].page; }), {});
+          });
+        }).then(function(bookmarkList) {
+          renderVars.bookmarkList = bookmarkList;
+
+          return Page.findListByCreator(userData, {limit: 10});
+        }).then(function(createdList) {
+          renderVars.createdList = createdList;
+          return Promise.resolve();
+        }).catch(function(err) {
+          debug('Error on finding user related entities', err);
+          // pass
+        });
       } else {
         return Promise.resolve();
       }
-    }).then(function(createdList) {
-      if (createdList) {
-        renderVars.createdList = createdList;
-      }
-
-      return Promise.resolve();
     }).then(function() {
       var defaultPageTeamplate = 'page';
-      if (userPage) {
+      if (userData) {
         defaultPageTeamplate = 'user_page';
       }
 

+ 6 - 7
lib/routes/user.js

@@ -23,16 +23,15 @@ module.exports = function(crowi, app) {
   api.checkUsername = function(req, res) {
     var username = req.query.username;
 
-    User.findUserByUsername(username, function(err, userData) {
+    User.findUserByUsername(username)
+    .then(function(userData) {
       if (userData) {
-        return res.json({
-          valid: false
-        });
+        return res.json({ valid: false });
       } else {
-        return res.json({
-          valid: true
-        });
+        return res.json({ valid: true });
       }
+    }).catch(function(err) {
+      return res.json({ valid: true });
     });
   };
 

+ 1 - 1
lib/views/layout/2column.html

@@ -144,7 +144,7 @@
 {% endblock %} {# layout_sidebar #}
 
 {% block layout_main %}
-<div id="main" class="main col-md-9 {% if page %}{{ css.grant(page) }}{% endif %}">
+<div id="main" class="main col-md-9 {% if page %}{{ css.grant(page) }}{% endif %} {% block main_css_class %}{% endblock %}">
   {% if page && page.grant != 1 %}
   <p class="page-grant">
     <i class="fa fa-lock"></i> {{ consts.pageGrants[page.grant] }} (このページの閲覧は制限されています)

+ 58 - 10
lib/views/user_page.html

@@ -1,17 +1,65 @@
 {% extends 'page.html' %}
 
-{% block content_head_before %}
-{% endblock %}
+{% block main_css_class %}user-page{% endblock %}
+
+{% block content_head %}
+
+{% if pageUser %}
 
-{% block content_main_before %}
-<div class="user-page-content row">
-  <div class="user-bookmark-list col-md-6">
-    <h2><i class="fa fa-star"></i> Bookmarks</h2>
-    {% include 'widget/page_list.html' with { pages: bookmarkList } %}
+<div class="header-wrap">
+  <h1 class="title" id="revision-path">{{ path }}</h1>
+  <div class="user-page-header">
+  {% if page %}
+    <a href="#" title="Bookmark" class="bookmark-link" id="bookmark-button" data-bookmarked="0"><i class="fa fa-star-o"></i></a>
+  {% endif %}
+    <div class="pull-left user-page-picture">
+      <img src="{{ pageUser|picture }}" class="picture picture-rounded">
+    </div>
+    <div class="user-page-meta">
+      <h2>{{ pageUser.name }}</h2>
+      <ul>
+        <li class="user-page-username"><i class="fa fa-user"></i> @{{ pageUser.username }}</li>
+        <li class="user-page-email"><i class="fa fa-envelope-o"></i> {{ pageUser.email }}</li>
+        {% if pageUser.introduction %}
+        <li class="user-page-introduction"><p>{{ pageUser.introduction|nl2br }}</p></li>
+        {% endif %}
+      </ul>
+    </div>
   </div>
-  <div class="user-created-list col-md-6">
-    <i class="fa fa-star"></i> Created
-    {% include 'widget/page_list.html' with { pages: createdList } %}
+
+
+  <div class="user-page-content">
+    <ul class="nav nav-tabs user-page-content-menu">
+      <li class="active">
+        <a href="#user-bookmark-list" data-toggle="tab"><i class="fa fa-star"></i> Bookmarks</a>
+      </li>
+      <li>
+        <a href="#user-created-list" data-toggle="tab"><i class="fa fa-pencil"></i> Recent Created</a>
+      </li>
+      <li>
+        <a href="/me"><i class="fa fa-gears"></i> Setting</a>
+      </li>
+    </ul>
+    <div class="user-page-content-tab tab-content">
+
+      <div class="tab-pane user-bookmark-list page-list active" id="user-bookmark-list">
+        <div class="page-list-container">
+        {% include 'widget/page_list.html' with { pages: bookmarkList } %}
+        </div>
+      </div>
+
+      <div class="tab-pane user-created-list page-list" id="user-created-list">
+        <div class="page-list-container">
+        {% include 'widget/page_list.html' with { pages: createdList } %}
+        </div>
+      </div>
+    </div>
   </div>
 </div>
+
+{% else %}
+  {% parent %}
+{% endif %}
+
 {% endblock %}
+

+ 0 - 1
lib/views/widget/page_list.html

@@ -12,7 +12,6 @@
       <span class="label label-info">PORTAL</span>
     {% endif  %}
 
-
     {% if page.commentCount > 0 %}
       <i class="fa fa-comment"></i>{{ page.commentCount }}
     {% endif  %}

+ 66 - 2
resource/css/_user.scss

@@ -1,4 +1,68 @@
+.crowi.main-container {
+  .main.user-page { // {{{ .main of layout related
+    padding: 0;
 
-.user-page {
-  background: #f0f0f0;
+    .header-wrap {
+      padding: 20px;
+
+      h1 {
+        margin: 0;
+        font-size: 1.1em;
+      }
+
+      .user-page-header {
+        margin-bottom: 16px;
+
+        .bookmark-link {
+          float: right;
+          color: #e6b422;
+          font-size: 2em;
+          &.bookmarked {
+            //color: #fff;
+          }
+        }
+
+        .user-page-picture {
+          img {
+            height: 64px;
+            width: 64px;
+          }
+        }
+        .user-page-meta {
+          padding-left: 88px;
+          color: #999;
+
+          h2 {
+            font-size: 2.5em;
+            color: #666;
+          }
+
+          ul {
+            padding-left: 0;
+            li {
+              list-style: none;
+            }
+          }
+          .user-page-username {
+            font-weight: bold;
+          }
+          .user-page-email {
+          }
+          .user-page-introduction {
+          }
+        }
+      }
+
+      .user-page-content-tab {
+        padding: 8px 0 8px;
+      }
+
+    }
+    .content-main {
+      padding: 20px;
+      &.on-edit {
+        padding: 0;
+      }
+    }
+  } // }}}
 }

+ 25 - 2
test/models/user.test.js

@@ -23,12 +23,35 @@ describe('User', function () {
       });
 
       it('should be found by findUserByUsername', function(done) {
-        User.findUserByUsername('aoi', function (err, userData) {
-          expect(err).to.be.null;
+        User.findUserByUsername('aoi')
+        .then(function(userData) {
           expect(userData).to.instanceof(User);
           done();
         });
       });
     });
   });
+
+  describe('User Utilities', function () {
+    context('Get username from path', function() {
+      it('found', function(done) {
+        var username = null;
+        username = User.getUsernameByPath('/user/sotarok');
+        expect(username).to.equal('sotarok');
+
+        username = User.getUsernameByPath('/user/some.user.name12/'); // with slash
+        expect(username).to.equal('some.user.name12');
+
+        done();
+      });
+
+      it('not found', function(done) {
+        var username = null;
+        username = User.getUsernameByPath('/the/page/is/not/related/to/user/page');
+        expect(username).to.be.null;
+
+        done();
+      });
+    });
+  });
 });