mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
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.
This commit is contained in:
parent
98d7278d08
commit
46cc691534
22 changed files with 161 additions and 227 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}}.
|
||||||
|
|
|
||||||
|
|
@ -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>)
|
||||||
|
|
|
||||||
|
|
@ -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 = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 }}")
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
.fa-check
|
||||||
.icon-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
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,7 @@ template(name="membersWidget")
|
||||||
| {{_ 'members'}}
|
| {{_ 'members'}}
|
||||||
.board-widget-content
|
.board-widget-content
|
||||||
each currentBoard.members
|
each currentBoard.members
|
||||||
+userAvatar(
|
+userAvatar(userId=this.userId showStatus=true)
|
||||||
userId=this.userId
|
|
||||||
draggable=true
|
|
||||||
size="small"
|
|
||||||
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>)
|
||||||
|
|
|
||||||
|
|
@ -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>)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,42 @@
|
||||||
template(name="userAvatar")
|
template(name="userAvatar")
|
||||||
.member.js-member(class="{{class}}" title="{{userData.profile.name}} ({{userData.username}})")
|
.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}})")
|
||||||
+avatar(user=userData size=size)
|
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
|
.info
|
||||||
h3.bottom
|
h3.bottom
|
||||||
a.js-profile(href="{{ pathFor route='Profile' username=user.username }}")= user.profile.name
|
a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
|
||||||
p.quiet.bottom @{{ user.username }}
|
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 }}")
|
a.js-show-mem-menu(href="{{pathFor route='Profile' username=user.username}}")
|
||||||
= user.profile.name
|
= 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
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
|
Meteor.subscribe('my-avatars');
|
||||||
|
|
||||||
Template.userAvatar.helpers({
|
Template.userAvatar.helpers({
|
||||||
userData: function() {
|
userData: function() {
|
||||||
if (! this.user) {
|
return Users.findOne(this.userId, {
|
||||||
this.user = Users.findOne(this.userId);
|
fields: {
|
||||||
}
|
profile: 1,
|
||||||
return this.user;
|
username: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
memberType: function() {
|
memberType: function() {
|
||||||
var userId = this.userId || this.user._id;
|
var user = Users.findOne(this.userId);
|
||||||
var user = Users.findOne(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');
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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')
|
|
||||||
});
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Avatar.options = {
|
|
||||||
fallbackType: 'initials'
|
|
||||||
};
|
|
||||||
27
collections/avatars.js
Normal file
27
collections/avatars.js
Normal file
|
|
@ -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;
|
||||||
|
});
|
||||||
|
|
@ -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
server/publications/avatars.js
Normal file
3
server/publications/avatars.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Meteor.publish('my-avatars', function() {
|
||||||
|
return Avatars.find({ userId: this.userId });
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue