2
0
Эх сурвалжийг харах

Re-factor the avatar system and support avatar uploads

The user is now able to upload an avatar, and pick one in a list.

This functionality should eventually be abstracted in a community
package but we still need to work on a great public API. We rely on
collectionFS to manage uploaded avatars. We also removed
bengott:avatar which was trying to solve the wrong problem (namely
displaying the avatar, which is as simple as displaying an image), and
not a avatar system as it should be.

Gravatar support is coming (back) soon. We may also want to have a
list of default fun avatars the user can choose instead of uploading
its own one.
Maxime Quandalle 10 жил өмнө
parent
commit
46cc691534

+ 1 - 0
.jshintrc

@@ -55,6 +55,7 @@
     "SubsManager": false,
     "SubsManager": false,
     "Mousetrap": false,
     "Mousetrap": false,
     "Avatar": true,
     "Avatar": true,
+    "Avatars": true,
     "Ps": true,
     "Ps": true,
     "Presence": true,
     "Presence": true,
     "Presences": true,
     "Presences": true,

+ 0 - 1
.meteor/packages

@@ -46,7 +46,6 @@ tmeasday:presence
 underscore
 underscore
 
 
 # UI components
 # UI components
-bengott:avatar
 fortawesome:fontawesome
 fortawesome:fontawesome
 linto:jquery-ui
 linto:jquery-ui
 mousetrap:mousetrap
 mousetrap:mousetrap

+ 0 - 5
.meteor/versions

@@ -6,7 +6,6 @@ alethes:pages@1.8.4
 audit-argument-checks@1.0.3
 audit-argument-checks@1.0.3
 autoupdate@1.2.1
 autoupdate@1.2.1
 base64@1.0.3
 base64@1.0.3
-bengott:avatar@0.7.6
 binary-heap@1.0.3
 binary-heap@1.0.3
 blaze@2.1.2
 blaze@2.1.2
 blaze-tools@1.0.3
 blaze-tools@1.0.3
@@ -52,9 +51,6 @@ iron:location@1.0.9
 iron:middleware-stack@1.0.9
 iron:middleware-stack@1.0.9
 iron:router@1.0.9
 iron:router@1.0.9
 iron:url@1.0.9
 iron:url@1.0.9
-jparker:crypto-core@0.1.0
-jparker:crypto-md5@0.1.1
-jparker:gravatar@0.3.1
 jquery@1.11.3_2
 jquery@1.11.3_2
 json@1.0.3
 json@1.0.3
 kenton:accounts-sandstorm@0.1.3
 kenton:accounts-sandstorm@0.1.3
@@ -107,7 +103,6 @@ softwarerero:accounts-t9n@1.0.9
 spacebars@1.0.6
 spacebars@1.0.6
 spacebars-compiler@1.0.6
 spacebars-compiler@1.0.6
 srp@1.0.3
 srp@1.0.3
-stylus@1.0.7
 tap:i18n@1.4.1
 tap:i18n@1.4.1
 templating@1.1.1
 templating@1.1.1
 tmeasday:presence@1.0.6
 tmeasday:presence@1.0.6

+ 3 - 3
client/components/activities/templates.html

@@ -1,7 +1,7 @@
 <template name="boardActivities">
 <template name="boardActivities">
     {{# each currentBoard.activities }}
     {{# each currentBoard.activities }}
         <div class="phenom phenom-action clearfix phenom-other">
         <div class="phenom phenom-action clearfix phenom-other">
-            {{> userAvatar user=user size="extra-small" class="creator js-show-mem-menu" }}
+            {{> userAvatar userId=user._id}}
             <div class="phenom-desc">
             <div class="phenom-desc">
                 {{ > memberName user=user }}
                 {{ > memberName user=user }}
 
 
@@ -86,7 +86,7 @@
 <template name="cardActivities">
 <template name="cardActivities">
     {{# each currentCard.comments }}
     {{# each currentCard.comments }}
         <div class="phenom phenom-action clearfix phenom-comment">
         <div class="phenom phenom-action clearfix phenom-comment">
-            {{> userAvatar user=user size="small" class="creator js-show-mem-menu" }}
+            {{> userAvatar userId=user._id}}
             <form>
             <form>
                 <div class="phenom-desc">
                 <div class="phenom-desc">
                     {{ > memberName user=user }}
                     {{ > memberName user=user }}
@@ -115,7 +115,7 @@
 
 
     {{# each currentCard.activities }}
     {{# each currentCard.activities }}
         <div class="phenom phenom-action clearfix phenom-other">
         <div class="phenom phenom-action clearfix phenom-other">
-            {{> userAvatar user=user size="extra-small" class="creator js-show-mem-menu" }}
+            {{> userAvatar userId=user._id size="extra-small" class="creator js-show-mem-menu" }}
             {{ > memberName user=user }}
             {{ > memberName user=user }}
             {{# if $eq activityType 'createCard' }}
             {{# if $eq activityType 'createCard' }}
                 {{_ 'activity-added' cardLabel list.title}}.
                 {{_ 'activity-added' cardLabel list.title}}.

+ 2 - 2
client/components/cards/details.jade

@@ -27,7 +27,7 @@ template(name="cardDetails")
       .card-details-item.card-details-item-members
       .card-details-item.card-details-item-members
         h3 {{_ 'members'}}
         h3 {{_ 'members'}}
         each members
         each members
-          +userAvatar(userId=this size="small" cardId=../_id)
+          +userAvatar(userId=this cardId=../_id)
         a.member.add-member.card-details-item-add-button.js-add-members
         a.member.add-member.card-details-item-add-button.js-add-members
           i.fa.fa-plus
           i.fa.fa-plus
 
 
@@ -86,7 +86,7 @@ template(name="cardMembersPopup")
     each board.members
     each board.members
       li.item(class="{{#if isCardMember}}active{{/if}}")
       li.item(class="{{#if isCardMember}}active{{/if}}")
         a.name.js-select-member(href="#")
         a.name.js-select-member(href="#")
-          +userAvatar(user=user size="small")
+          +userAvatar(userId=user._id)
           span.full-name
           span.full-name
             = user.profile.name
             = user.profile.name
             | (<span class="username">{{ user.username }}</span>)
             | (<span class="username">{{ user.username }}</span>)

+ 2 - 2
client/components/cards/events.js

@@ -134,7 +134,7 @@ Template.createLabelPopup.events({
   'submit .create-label': function(evt, tpl) {
   'submit .create-label': function(evt, tpl) {
     var name = tpl.$('#labelName').val().trim();
     var name = tpl.$('#labelName').val().trim();
     var boardId = Session.get('currentBoard');
     var boardId = Session.get('currentBoard');
-    var selectLabelDom = tpl.$('.js-palette-select:not(.hide)').get(0);
+    var selectLabelDom = tpl.$('.js-palette-select').get(0);
     var selectLabel = Blaze.getData(selectLabelDom);
     var selectLabel = Blaze.getData(selectLabelDom);
     Boards.update(boardId, {
     Boards.update(boardId, {
       $push: {
       $push: {
@@ -166,7 +166,7 @@ Template.editLabelPopup.events({
     var name = tpl.$('#labelName').val().trim();
     var name = tpl.$('#labelName').val().trim();
     var boardId = Session.get('currentBoard');
     var boardId = Session.get('currentBoard');
     var getLabel = Utils.getLabelIndex(boardId, this._id);
     var getLabel = Utils.getLabelIndex(boardId, this._id);
-    var selectLabelDom = tpl.$('.js-palette-select:not(.hide)').get(0);
+    var selectLabelDom = tpl.$('.js-palette-select').get(0);
     var selectLabel = Blaze.getData(selectLabelDom);
     var selectLabel = Blaze.getData(selectLabelDom);
     var $set = {};
     var $set = {};
 
 

+ 1 - 1
client/components/cards/minicard.jade

@@ -10,7 +10,7 @@ template(name="minicard")
     if members
     if members
       .minicard-members.js-minicard-members
       .minicard-members.js-minicard-members
         each members
         each members
-          +userAvatar(userId=this size="small" cardId="{{../_id}}")
+          +userAvatar(userId=this)
     .badges
     .badges
       if comments.count
       if comments.count
         .badge(title="{{_ 'card-comments-title' comments.count }}")
         .badge(title="{{_ 'card-comments-title' comments.count }}")

+ 3 - 3
client/components/cards/templates.html

@@ -1,7 +1,7 @@
 <template name="cardMemberPopup">
 <template name="cardMemberPopup">
     <div class="board-member-menu">
     <div class="board-member-menu">
         <div class="mini-profile-info">
         <div class="mini-profile-info">
-            {{> userAvatar user=user }}
+            {{> userAvatar userId=user._id }}
             <div class="info">
             <div class="info">
                 <h3 class="bottom" style="margin-right: 40px;">
                 <h3 class="bottom" style="margin-right: 40px;">
                     <a class="js-profile" href="{{ pathFor route='Profile' username=user.username }}">{{ user.profile.name }}</a>
                     <a class="js-profile" href="{{ pathFor route='Profile' username=user.username }}">{{ user.profile.name }}</a>
@@ -125,7 +125,7 @@
                             <h3 class="card-details-item-header">{{_ 'members'}}</h3>
                             <h3 class="card-details-item-header">{{_ 'members'}}</h3>
                             <div class="js-card-details-members-list clearfix">
                             <div class="js-card-details-members-list clearfix">
                                 {{# each card.members }}
                                 {{# each card.members }}
-                                    {{> userAvatar userId=this size="small" cardId=../card._id }}
+                                    {{> userAvatar userId=this}}
                                 {{/ each }}
                                 {{/ each }}
                                 <a class="card-details-item-add-button dark-hover js-details-edit-members">
                                 <a class="card-details-item-add-button dark-hover js-details-edit-members">
                                     <span class="icon-sm fa fa-plus"></span>
                                     <span class="icon-sm fa fa-plus"></span>
@@ -196,7 +196,7 @@
         </div>
         </div>
         {{# if currentUser.isBoardMember }}
         {{# if currentUser.isBoardMember }}
             <div class="new-comment js-new-comment">
             <div class="new-comment js-new-comment">
-                {{> userAvatar user=currentUser size="small" class="member-no-menu" }}
+                {{> userAvatar userId=currentUser._id}}
                 <form id="CommentForm">
                 <form id="CommentForm">
                     {{#editor class="new-comment-input js-new-comment-input"}}{{/editor}}
                     {{#editor class="new-comment-input js-new-comment-input"}}{{/editor}}
                     <div class="add-controls clearfix">
                     <div class="add-controls clearfix">

+ 3 - 0
client/components/forms/forms.styl

@@ -156,6 +156,9 @@ button
       box-shadow: 0 1px 0 #4d4d4d
       box-shadow: 0 1px 0 #4d4d4d
       color: #fff
       color: #fff
 
 
+  i.fa
+    margin-right: 10px
+
 input[type="submit"].disabled,
 input[type="submit"].disabled,
 input[type="submit"]:disabled,
 input[type="submit"]:disabled,
 input[type="button"].disabled,
 input[type="button"].disabled,

+ 1 - 1
client/components/lists/main.js

@@ -25,7 +25,7 @@ BlazeComponent.extendComponent({
       return;
       return;
 
 
     var boardComponent = self.componentParent();
     var boardComponent = self.componentParent();
-    var itemsSelector = '.js-minicard:not(.placeholder, .hide, .js-composer)';
+    var itemsSelector = '.js-minicard:not(.placeholder, .js-composer)';
     var $cards = self.$('.js-minicards');
     var $cards = self.$('.js-minicards');
     $cards.sortable({
     $cards.sortable({
       connectWith: '.js-minicards',
       connectWith: '.js-minicards',

+ 4 - 70
client/components/main/popup.styl

@@ -13,7 +13,7 @@
 
 
   hr
   hr
     margin: 4px -10px
     margin: 4px -10px
-    width: 275px + 2*10px
+    width: 300px
 
 
   input[type="text"],
   input[type="text"],
   input[type="email"],
   input[type="email"],
@@ -122,12 +122,8 @@
   margin-bottom: 8px
   margin-bottom: 8px
 
 
 .pop-over-list
 .pop-over-list
-  &.navigable li.not-selectable>a:hover,
-  li.not-selectable>a:hover
-    color: #8c8c8c
-    cursor: default
-
   li > a
   li > a
+    clear: both
     cursor: pointer
     cursor: pointer
     display: block
     display: block
     font-weight: 700
     font-weight: 700
@@ -153,7 +149,6 @@
         background: #fff
         background: #fff
 
 
     .sub-name
     .sub-name
-      clear: both
       color: #8c8c8c
       color: #8c8c8c
       display: block
       display: block
       font-size: 12px
       font-size: 12px
@@ -164,21 +159,6 @@
     &.current
     &.current
       background-color: #e2e6e9
       background-color: #e2e6e9
 
 
-    .unread-indicator
-      background: #2e85b8
-      background: linear-gradient(to bottom, #2e85b8 0, #2b7cab 100%)
-      border-radius: 7px
-      display: block
-      height: 14px
-      opacity: 0
-      position: absolute
-      right: 16px
-      top: 8px
-      width: 14px
-
-      &.any
-        opacity: 1
-
     &:active
     &:active
       background-color: #2e85b8
       background-color: #2e85b8
 
 
@@ -204,8 +184,7 @@
     margin: 0
     margin: 0
 
 
   .pop-over-list.checkable
   .pop-over-list.checkable
-
-    .icon-check
+    .fa-check
       display: none
       display: none
       position: absolute
       position: absolute
       top: 6px
       top: 6px
@@ -214,54 +193,9 @@
     li.active a
     li.active a
       padding-right: 28px
       padding-right: 28px
 
 
-      .icon-check
+      .fa-check
         display: block
         display: block
 
 
-    &.left-check
-
-      .icon-check
-        right: auto
-        left: 10px
-
-      li a
-        padding-right: 10px
-        padding-left: 30px
-
-      li.active a
-        padding-right: 10px
-
-  &.normal-weight li>a
-    font-weight: 400
-
-  &.navigable
-
-    li > a:hover
-      background-color: transparent
-      color: #4d4d4d
-
-      .sub-name,
-      .quiet
-        color: #8c8c8c
-
-    li.selected > a
-      background-color: #005377
-      color: #fff
-
-      .sub-name,
-      .quiet
-        color: #eee
-
-    li.selected > a
-
-      &.current
-        background-color: #005377
-
-      .unread-indicator
-        background: #fff
-
-      &:active
-        background-color: #005377
-
 .pop-over.miniprofile
 .pop-over.miniprofile
   .header
   .header
     border-bottom-color: transparent
     border-bottom-color: transparent

+ 2 - 6
client/components/sidebar/sidebar.jade

@@ -27,11 +27,7 @@ template(name="membersWidget")
       | {{_ 'members'}}
       | {{_ 'members'}}
     .board-widget-content
     .board-widget-content
       each currentBoard.members
       each currentBoard.members
-        +userAvatar(
-          userId=this.userId
-          draggable=true
-          size="small"
-          showStatus=true)
+        +userAvatar(userId=this.userId showStatus=true)
       unless isSandstorm
       unless isSandstorm
         if currentUser.isBoardAdmin
         if currentUser.isBoardAdmin
           a.member.add-member.js-manage-board-members
           a.member.add-member.js-manage-board-members
@@ -86,7 +82,7 @@ template(name="addMemberPopup")
     +esEach(index="users")
     +esEach(index="users")
       li.item.js-member-item(class="{{#if isBoardMember }}disabled{{/if}}")
       li.item.js-member-item(class="{{#if isBoardMember }}disabled{{/if}}")
         a.name.js-select-member(title="{{ profile.name }} ({{ username }})")
         a.name.js-select-member(title="{{ profile.name }} ({{ username }})")
-          +userAvatar(user=this size="small")
+          +userAvatar(userId=_id)
           span.full-name
           span.full-name
             = profile.name
             = profile.name
             | (<span class="username">{{ username }}</span>)
             | (<span class="username">{{ username }}</span>)

+ 1 - 1
client/components/sidebar/sidebarFilters.jade

@@ -23,7 +23,7 @@ template(name="filterSidebar")
           with getUser userId
           with getUser userId
             li(class="{{#if Filter.members.isSelected _id}}active{{/if}}")
             li(class="{{#if Filter.members.isSelected _id}}active{{/if}}")
               a.name.js-toogle-member-filter
               a.name.js-toogle-member-filter
-                +userAvatar(user=this size="small")
+                +userAvatar(userId=this._id)
                 span.sidebar-list-item-description
                 span.sidebar-list-item-description
                   = profile.name
                   = profile.name
                   | (<span class="username">{{ username }}</span>)
                   | (<span class="username">{{ username }}</span>)

+ 29 - 9
client/components/users/userAvatar.jade

@@ -1,22 +1,42 @@
 template(name="userAvatar")
 template(name="userAvatar")
-  .member.js-member(class="{{class}}" title="{{userData.profile.name}} ({{userData.username}})")
-    +avatar(user=userData size=size)
+  .member.js-member(title="{{userData.profile.fullname}} ({{userData.username}})")
+    if userData.profile.avatarUrl
+      img.avatar.avatar-image(src=userData.profile.avatarUrl)
     if showStatus
     if showStatus
       span.member-presence-status(class=presenceStatusClassName)
       span.member-presence-status(class=presenceStatusClassName)
       span.member-type(class=memberType)
       span.member-type(class=memberType)
 
 
-
 template(name="userPopup")
 template(name="userPopup")
   .board-member-menu
   .board-member-menu
     .mini-profile-info
     .mini-profile-info
       +userAvatar(user=user)
       +userAvatar(user=user)
-        .info
-          h3.bottom
-            a.js-profile(href="{{ pathFor route='Profile' username=user.username }}")= user.profile.name
-            p.quiet.bottom @{{ user.username }}
+      .info
+        h3.bottom
+          a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
+          p.quiet.bottom @{{ user.username }}
 
 
 template(name="memberName")
 template(name="memberName")
-  a.inline-object.js-show-mem-menu(href="{{ pathFor route='Profile' username=user.username }}")
-    = user.profile.name
+  a.js-show-mem-menu(href="{{pathFor route='Profile' username=user.username}}")
+    = user.profile.fullname
     if username
     if username
       | ({{ user.username }})
       | ({{ user.username }})
+
+template(name="changeAvatarPopup")
+  ul.pop-over-list
+    each uploadedAvatars
+      li: a.js-select-avatar
+        .member: .avatar
+          img.avatar-image(src="{{url avatarUrlOptions}}")
+        | Uploaded avatar
+        if isSelected
+          i.fa.fa-check
+        p.sub-name
+          unless isSelected
+            a.js-delete-avatar
+              | Delete
+            |  -
+          = original.name
+  input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
+  button.full.js-upload-avatar
+    i.fa.fa-upload
+    | Upload an avatar

+ 77 - 7
client/components/users/userAvatar.js

@@ -1,17 +1,22 @@
+Meteor.subscribe('my-avatars');
+
 Template.userAvatar.helpers({
 Template.userAvatar.helpers({
   userData: function() {
   userData: function() {
-    if (! this.user) {
-      this.user = Users.findOne(this.userId);
-    }
-    return this.user;
+    return Users.findOne(this.userId, {
+      fields: {
+        profile: 1,
+        username: 1
+      }
+    });
   },
   },
+
   memberType: function() {
   memberType: function() {
-    var userId = this.userId || this.user._id;
-    var user = Users.findOne(userId);
+    var user = Users.findOne(this.userId);
     return user && user.isBoardAdmin() ? 'admin' : 'normal';
     return user && user.isBoardAdmin() ? 'admin' : 'normal';
   },
   },
+
   presenceStatusClassName: function() {
   presenceStatusClassName: function() {
-    var userPresence = Presences.findOne({ userId: this.user._id });
+    var userPresence = Presences.findOne({ userId: this.userId });
     if (! userPresence)
     if (! userPresence)
       return 'disconnected';
       return 'disconnected';
     else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
     else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
@@ -20,3 +25,68 @@ Template.userAvatar.helpers({
       return 'idle';
       return 'idle';
   }
   }
 });
 });
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'changeAvatarPopup';
+  },
+
+  avatarUrlOptions: function() {
+    return {
+      auth: false,
+      brokenIsFine: true
+    };
+  },
+
+  uploadedAvatars: function() {
+    return Avatars.find({userId: Meteor.userId()});
+  },
+
+  isSelected: function() {
+    var userProfile = Meteor.user().profile;
+    var avatarUrl = userProfile && userProfile.avatarUrl;
+    var currentAvatarUrl = this.currentData().url(this.avatarUrlOptions());
+    return avatarUrl === currentAvatarUrl;
+  },
+
+  setAvatar: function(avatarUrl) {
+    Meteor.users.update(Meteor.userId(), {
+      $set: {
+        'profile.avatarUrl': avatarUrl
+      }
+    });
+  },
+
+  events: function() {
+    return [{
+      'click .js-upload-avatar': function() {
+        this.$('.js-upload-avatar-input').click();
+      },
+      'change .js-upload-avatar-input': function(evt) {
+        var self = this;
+        var file, fileUrl;
+
+        FS.Utility.eachFile(evt, function(f) {
+          file = Avatars.insert(new FS.File(f));
+          fileUrl = file.url(self.avatarUrlOptions());
+        });
+        var fetchAvatarInterval = window.setInterval(function() {
+          $.ajax({
+            url: fileUrl,
+            success: function() {
+              self.setAvatar(file.url(self.avatarUrlOptions()));
+              window.clearInterval(fetchAvatarInterval);
+            }
+          });
+        }, 100);
+      },
+      'click .js-select-avatar': function() {
+        var avatarUrl = this.currentData().url(this.avatarUrlOptions());
+        this.setAvatar(avatarUrl);
+      },
+      'click .js-delete-avatar': function() {
+        Avatars.remove(this.currentData()._id);
+      }
+    }];
+  }
+}).register('changeAvatarPopup');

+ 1 - 3
client/components/users/userHeader.jade

@@ -7,7 +7,7 @@ template(name="headerUserBar")
       else
       else
         = currentUser.username
         = currentUser.username
     a.header-user-bar-avatar.js-change-avatar
     a.header-user-bar-avatar.js-change-avatar
-      +userAvatar(user=currentUser)
+      +userAvatar(userId=currentUser._id)
 
 
 template(name="memberMenuPopup")
 template(name="memberMenuPopup")
   ul.pop-over-list
   ul.pop-over-list
@@ -33,8 +33,6 @@ template(name="editProfilePopup")
       input.js-profile-initials(type="text" value=profile.initials)
       input.js-profile-initials(type="text" value=profile.initials)
     input.primary.wide(type="submit" value="{{_ 'save'}}")
     input.primary.wide(type="submit" value="{{_ 'save'}}")
 
 
-template(name="changeAvatarPopup")
-
 template(name="changePasswordPopup")
 template(name="changePasswordPopup")
   +atForm(state='changePwd')
   +atForm(state='changePwd')
 
 

+ 0 - 79
client/components/users/userProfile.html

@@ -1,79 +0,0 @@
-<template name='profile'>
-    {{ # if profile }}
-        <div class="tabbed-pane-header">
-            <div class="tabbed-pane-header-wrapper clearfix">
-                <a class="tabbed-pane-header-image profile-image ed js-change-avatar-profile" href="#">
-                    {{> userAvatar user=profile size="large"}}
-                </a>
-                <div class="tabbed-pane-header-details">
-                    <div class="js-current-details">
-                        <div class="tabbed-pane-header-details-name">
-                            <h1 class="inline"> {{ profile.profile.name }} </h1>
-                            <p class="window-title-extra quiet"> @{{ profile.username }} </p>
-                        </div>
-                        <div class="tabbed-pane-header-details-content">
-                            <p>{{ profile.profile.bio }}</p>
-                        </div>
-                        <div class="tabbed-pane-header-details-content"></div>
-                    </div>
-                    {{ > profileEditForm }}
-                </div>
-            </div>
-        </div>
-    {{ else }}
-        {{ > message label='user-profile-not-found' }}
-    {{ /if }}
-</template>
-
-<template name="settings">
-    {{ > profile profile=currentUser }}
-    <div class="tabbed-pane-main-col clearfix">
-        <div class="tabbed-pane-main-col-loading hide js-loading-page">
-            <span class="tabbed-pane-main-col-loading-spinner spinner"></span>
-        </div>
-        <div class="tabbed-pane-main-col-wrapper js-content">
-            <div class="window-module clearfix">
-                <div class="window-module-title">
-                    <h3>{{_ "account-details"}}</h3>
-                </div>
-                <a class="big-link js-change-name-and-bio" href="#">
-                    <span class="text">{{_ 'change-name-initials-bio'}}</span>
-                </a>
-                <a class="big-link js-change-avatar" href="#">
-                    <span class="text">{{_ 'change-avatar'}}</span>
-                </a>
-                <a class="big-link js-change-password" href="#">
-                    <span class="text">{{_ 'change-password'}}</span>
-                </a>
-                <a class="big-link js-change-email" href="#">
-                    <span class="text">{{_ 'change-email'}}</span>
-                </a>
-            </div>
-        </div>
-    </div>
-</template>
-
-<template name="profileEditForm">
-    {{#if $eq currentUser.username profile.username }}
-        {{# if session 'ProfileEditForm' }}
-            <form id="ProfileEditForm" class="js-profile-form">
-                <p class="error js-profile-form-error hide"></p>
-                <label>{{_ "username"}}</label>
-                <input type="text" id="username" value="{{ profile.username }}" disabled>
-                <label>{{_ "fullname"}}</label>
-                <input type="text" id="name" value="{{ profile.profile.name }}">
-                <label>
-                    {{_ "bio"}} <span class="quiet">({{_ 'optional'}})</span>
-                </label>
-                <textarea id="bio">{{ profile.profile.bio }}</textarea>
-                <input type="submit" class="primary wide js-submit-profile" value="{{_ 'save'}}">
-                <input type="button" class="js-cancel-edit-profile" value="{{_ 'cancel'}}">
-            </form>
-        {{ else }}
-            <a class="button-link tabbed-pane-header-details-edit js-edit-profile" href="#">
-                <span class="icon-sm fa fa-pencil"></span>
-                {{_ "edit-profile"}}
-            </a>
-        {{ /if }}
-    {{ /if }}
-</template>

+ 0 - 31
client/components/users/userProfile.js

@@ -1,31 +0,0 @@
-Template.profileEditForm.events({
-  'click .js-edit-profile': function() {
-    Session.set('ProfileEditForm', true);
-  },
-  'click .js-cancel-edit-profile': function() {
-    Session.set('ProfileEditForm', false);
-  },
-  'submit #ProfileEditForm': function(evt, t) {
-    var name = t.find('#name').value;
-    var bio = t.find('#bio').value;
-
-    // trim and update
-    if ($.trim(name)) {
-      Users.update(this.profile()._id, {
-        $set: {
-          'profile.name': name,
-          'profile.bio': bio
-        }
-      }, function() {
-
-        // update complete close profileEditForm
-        Session.set('ProfileEditForm', false);
-      });
-    }
-    evt.preventDefault();
-  }
-});
-
-Template.memberName.events({
-  'click .js-show-mem-menu': Popup.open('user')
-});

+ 0 - 3
client/config/avatar.js

@@ -1,3 +0,0 @@
-Avatar.options = {
-  fallbackType: 'initials'
-};

+ 27 - 0
collections/avatars.js

@@ -0,0 +1,27 @@
+Avatars = new FS.Collection('avatars', {
+  stores: [
+    new FS.Store.GridFS('avatars')
+  ],
+  filter: {
+    maxSize: 32000,
+    allow: {
+      contentTypes: ['image/*']
+    }
+  }
+});
+
+var isOwner = function(userId, file) {
+  return userId && userId === file.userId;
+};
+
+Avatars.allow({
+  insert: isOwner,
+  update: isOwner,
+  remove: isOwner,
+  download: function() { return true; },
+  fetch: ['userId']
+});
+
+Avatars.files.before.insert(function(userId, doc) {
+  doc.userId = userId;
+});

+ 1 - 0
collections/users.js

@@ -43,6 +43,7 @@ Users.helpers({
 
 
 Meteor.methods({
 Meteor.methods({
   setUsername: function(username) {
   setUsername: function(username) {
+    check(username, String);
     var nUsersWithUsername = Users.find({username: username}).count();
     var nUsersWithUsername = Users.find({username: username}).count();
     if (nUsersWithUsername > 0) {
     if (nUsersWithUsername > 0) {
       throw new Meteor.Error('username-already-taken');
       throw new Meteor.Error('username-already-taken');

+ 3 - 0
server/publications/avatars.js

@@ -0,0 +1,3 @@
+Meteor.publish('my-avatars', function() {
+  return Avatars.find({ userId: this.userId });
+});