From c21e096724ccd662a268c2ba302668b6f3107000 Mon Sep 17 00:00:00 2001
From: Pepf
Date: Fri, 18 Jul 2014 13:10:24 +0200
Subject: [PATCH 1/3] Playlist privacy features
---
client/lib/globalhelpers.js | 8 +++
client/views/playlists.html | 76 +++++++++++++++++++---------
client/views/playlists.js | 37 ++++++++++++++
client/views/userProfile.html | 14 +++--
client/views/userProfile.js | 13 ++++-
collections.js => lib/collections.js | 10 +++-
lib/router.js | 4 +-
server/lib/publish.js | 7 +--
server/permissions.js | 25 ++++++++-
server/serverMethods.js | 10 ++++
10 files changed, 166 insertions(+), 38 deletions(-)
rename collections.js => lib/collections.js (91%)
create mode 100644 server/serverMethods.js
diff --git a/client/lib/globalhelpers.js b/client/lib/globalhelpers.js
index 7746194..c96a32a 100644
--- a/client/lib/globalhelpers.js
+++ b/client/lib/globalhelpers.js
@@ -31,6 +31,14 @@ Handlebars.registerHelper('username', function(user){
}
});
+Handlebars.registerHelper('privacyIcon', function(playlist) {
+ var icon = "globe";
+ if(this.privacy == "viewonly") { icon = "eye-open"; }
+ if(this.privacy == "private") { icon = "lock"; }
+
+ return '';
+});
+
Template.userProfile.avatarUrl = function() {
if(this.profile.avatar) {
return this.profile.avatar;
diff --git a/client/views/playlists.html b/client/views/playlists.html
index 336b783..300dbb7 100644
--- a/client/views/playlists.html
+++ b/client/views/playlists.html
@@ -1,15 +1,19 @@
-
-
-
Newest playlists
-
-
- {{#each playlists}}
- {{> playlistsEntry}}
- {{/each}}
-
-
- View all playlists
+
+
+
+
Newest playlists
+
+
+ {{#each playlists}}
+ {{> playlistsEntry}}
+ {{/each}}
+
+
+
+
Or add a new one
{{> insertPlaylistForm}}
@@ -31,7 +35,25 @@
All playlists
- {{> quickForm collection="Playlists" id="insertPlaylistForm" type="insert" fields="title,cover,tags,description"}}
+{{#autoForm collection="Playlists" id="insertPlaylistForm" type="insert" }}
+ {{> afQuickField name="title"}}
+ {{> afQuickField name="cover"}}
+ {{> afQuickField name="tags" }}
+ {{> afFieldInput name="privacy" type="hidden"}}
+
+
+
+
+
+
+
+{{/autoForm}}
@@ -50,7 +72,7 @@ All playlists
edit
-
{{title}}
+
{{{privacyIcon this}}} {{title}}
{{#if description}}
{{description}}
{{/if}}
@@ -127,16 +149,7 @@
Stats
{{#if isOwner}}
-
-
-
Owner's panel
-
-
-
You are the owner of this playlist, so you can view fancy settings
-
-
-
-
+ {{>ownerPanel}}
{{/if}}
@@ -150,6 +163,21 @@ Owner's panel
{{> quickForm collection="Playlists" doc=editingDoc id="updatePlaylistForm" type="update" fields="title,cover,tags,description"}}
+
+
+
+
Owner's panel
+
+
+
You are the owner of this playlist, so you can view fancy settings
+
+ {{#afDeleteButton collection="Playlists" doc=editingDoc id="removeButton"}}
+
Delete playlist
+ {{/afDeleteButton}}
+
+
+
+
@@ -175,8 +203,6 @@ Owner's panel
{{> searchResults}}
-
-
{{> songs}}
diff --git a/client/views/playlists.js b/client/views/playlists.js
index 2ce1ae8..5d5423e 100644
--- a/client/views/playlists.js
+++ b/client/views/playlists.js
@@ -1,3 +1,35 @@
+Template.insertPlaylistForm.rendered = function() {
+ Session.setDefault('playlistPrivacy','public');
+ this.find('[data-schema-key=privacy]').value = 'public';
+ $("button").tooltip();
+ AutoForm.hooks({
+ insertPlaylistForm: {
+ onSuccess: function(operation, result, template) {
+ Router.go('/playlist/'+result);
+ }
+ }
+ });
+}
+
+Template.insertPlaylistForm.events = {
+ 'click #privacyToggle button' : function(e,template) {
+ var btn = e.currentTarget;
+ Session.set('playlistPrivacy',btn.getAttribute("data-action"));
+ template.find('[data-schema-key=privacy]').value = btn.getAttribute("data-action");
+ }
+}
+
+
+Template.playlist.rendered = function() {
+ AutoForm.hooks({
+ removeButton: { //on successful remove, go back to playlist page
+ onSuccess: function(operation, result, template) {
+ Router.go('playlists');
+ }
+ }
+ });
+}
+
Template.playlist.selected = function () {
return Session.equals("playing_song", this._id) ? "selected" : '';
};
@@ -17,6 +49,7 @@ Template.playlist.playing = function () {
return false;
}
};
+
Template.playlist.songCount = function() {
return this.songs ? this.songs.length : 0;
};
@@ -207,6 +240,10 @@ Template.updatePlaylistForm.editingDoc = function () {
return Playlists.findOne({_id: this._id});
};
+Template.ownerPanel.editingDoc = function () {
+ return Playlists.findOne({_id: this._id});
+};
+
// initialize active_tab
Session.set('active_tab', 'songs');
diff --git a/client/views/userProfile.html b/client/views/userProfile.html
index c76feda..69b5147 100644
--- a/client/views/userProfile.html
+++ b/client/views/userProfile.html
@@ -34,11 +34,19 @@ Hey {{this.username}},
On top of that, you have already liked {{profile.lovedSongs.length}} songs, keep up the good work!
- Your playlists
- If we are correct, these are playlists made by you
+ Public playlists
+ If we are correct, these are playlists made by you and visible by everyone
{{#each userPlaylists}}
{{>userPlaylist}}
{{/each}}
+ Private playlists
+ {{#if privatePlaylists}}Additionally, you have some private playlists
+ {{else}}
+ You have no private playlists yet
+ {{/if}}
+ {{#each privatePlaylists}}
+ {{>userPlaylist}}
+ {{/each}}
{{else}}
@@ -53,7 +61,7 @@
{{this.username}}
-
+
{{songCount}}
created {{fromNow createdAt}}
diff --git a/client/views/userProfile.js b/client/views/userProfile.js
index 21df940..f42e4e6 100644
--- a/client/views/userProfile.js
+++ b/client/views/userProfile.js
@@ -1,9 +1,18 @@
+Template.userProfile.created = function() {
+ Meteor.call('getPrivatePlaylists', function(error,result) {
+ if(error) { console.log(error); return; }
+ Session.set("privatePlaylists", result);
+ });
+
+ Session.setDefault("showEditAvatarBar",false);
+}
+
Template.userProfile.isOwner = function() {
return Meteor.userId() == this._id;
};
-Template.userProfile.rendered = function() {
- Session.setDefault("showEditAvatarBar",false);
+Template.userProfile.privatePlaylists = function() {
+ return Session.get("privatePlaylists");
};
Template.userProfile.userFriends = function() {
diff --git a/collections.js b/lib/collections.js
similarity index 91%
rename from collections.js
rename to lib/collections.js
index a858729..b517271 100644
--- a/collections.js
+++ b/lib/collections.js
@@ -146,7 +146,15 @@ Playlists = new Meteor.Collection('playlists', {
optional: true,
max: 500
},
-
+ privacy: {
+ type: String,
+ //public: everyone can edit
+ //viewonly: everyone can see playlist, but can't edit
+ //private: other users other than owner can't see the playlist
+ label: "Set playlist privacy level",
+ allowedValues: ['public', 'viewonly', 'private'],
+ defaultValue: 'public'
+ },
songs: {
type: [Object],
optional: true,
diff --git a/lib/router.js b/lib/router.js
index 8548374..5de9308 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -83,11 +83,9 @@ Router.map(function() {
data: function () {
var user = Meteor.users.findOne(this.params._id);
- var playlists = Playlists.find( {owner: this.params._id} );
+ var playlists = Playlists.find( {owner: this.params._id});
user.userPlaylists = playlists;
return user;
},
});
-
- this.route('friends');
});
diff --git a/server/lib/publish.js b/server/lib/publish.js
index 067eeaf..0bab4c0 100644
--- a/server/lib/publish.js
+++ b/server/lib/publish.js
@@ -3,8 +3,9 @@ Meteor.publish("allusers", function () {
{fields: {'username': 1, 'profile': 1, 'createdAt':1}});
});
+//In all playlists view, hide the private ones
Meteor.publish("allplaylists", function () {
- return Playlists.find();
+ return Playlists.find({'privacy' : { $ne: 'private'}});
});
Meteor.publish("playlist", function (id) {
@@ -17,9 +18,9 @@ Meteor.publish("songs", function (ids) {
return Songs.find({_id: {$in: ids}});
});
-//Playlists from one user
+//Playlists from one user, including private ones
Meteor.publish("playlistsByUser", function(userId) {
- return Playlists.find({owner:userId},
+ return Playlists.find({owner:userId, privacy : 'private'},
{ sort: {createdAt: 1},
fields: { 'title':1,
'createdAt':1,
diff --git a/server/permissions.js b/server/permissions.js
index c999b5f..0387ce2 100644
--- a/server/permissions.js
+++ b/server/permissions.js
@@ -20,4 +20,27 @@ Meteor.users.allow({
return false;
},
fetch: ['username']
-});
\ No newline at end of file
+});
+
+Playlists.allow({
+ //Everyone can create new playlists
+ insert: function(userId, doc) {
+ return true;
+ },
+ //only check if we're updating songs; if so, find out if this allowed
+ update: function (userId, doc, fields, modifier) {
+ if( _.contains(fields, 'songs')) {
+ if(doc.privacy == "public") { return true; }
+ else if((doc.privacy == "viewonly" || doc.privacy == "private") && doc.owner == userId) { return true; }
+ else { return false; }
+ }
+ //for now, other fields can be edited without problems...
+ return true;
+ },
+ //only remove if owner equals user id
+ remove: function (userId, doc) {
+ return doc.owner == userId;
+ },
+ //important fields for permissions
+ fetch: ['owner','privacy']
+ });
diff --git a/server/serverMethods.js b/server/serverMethods.js
new file mode 100644
index 0000000..5ec5d85
--- /dev/null
+++ b/server/serverMethods.js
@@ -0,0 +1,10 @@
+Meteor.methods({
+ //retrieves all playlists set to "private" from currently logged in user
+ getPrivatePlaylists: function () {
+ if (!this.userId)
+ throw new Meteor.Error(404, "No one is logged in");
+
+ //return as static object, just to check if everything works
+ return Playlists.find({'owner':this.userId, 'privacy' : 'private'}).fetch();
+ }
+});
\ No newline at end of file
From 56fc6a4d17e50a9df49960afa5f1a0ce66d38029 Mon Sep 17 00:00:00 2001
From: Pepf
Date: Fri, 18 Jul 2014 13:27:18 +0200
Subject: [PATCH 2/3] Tweak user permissions and playlist privacy
---
server/lib/publish.js | 2 +-
server/permissions.js | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/server/lib/publish.js b/server/lib/publish.js
index 0bab4c0..d352d05 100644
--- a/server/lib/publish.js
+++ b/server/lib/publish.js
@@ -20,7 +20,7 @@ Meteor.publish("songs", function (ids) {
//Playlists from one user, including private ones
Meteor.publish("playlistsByUser", function(userId) {
- return Playlists.find({owner:userId, privacy : 'private'},
+ return Playlists.find({owner:userId},
{ sort: {createdAt: 1},
fields: { 'title':1,
'createdAt':1,
diff --git a/server/permissions.js b/server/permissions.js
index 0387ce2..7936f24 100644
--- a/server/permissions.js
+++ b/server/permissions.js
@@ -29,11 +29,13 @@ Playlists.allow({
},
//only check if we're updating songs; if so, find out if this allowed
update: function (userId, doc, fields, modifier) {
+ console.log(userId, " wants to modify ", fields, " of ", doc.privacy, " playlist by ", doc.owner);
if( _.contains(fields, 'songs')) {
if(doc.privacy == "public") { return true; }
else if((doc.privacy == "viewonly" || doc.privacy == "private") && doc.owner == userId) { return true; }
- else { return false; }
+ else { console.log("this user can't edit these playlists"); return false; }
}
+ console.log("not editing songs");
//for now, other fields can be edited without problems...
return true;
},
From da8c4f40b5c1ca53844618314b52ff7df970d4d6 Mon Sep 17 00:00:00 2001
From: Pepf
Date: Sat, 26 Jul 2014 01:10:27 +0200
Subject: [PATCH 3/3] More UI polishing
---
client/lib/globalhelpers.html | 7 +++
client/lib/globalhelpers.js | 13 +++--
client/stylesheets/fusic.import.less | 10 ++++
client/views/playlist.css | 6 ++
client/views/playlists.html | 83 +++++++++++++---------------
client/views/playlists.js | 23 ++++++--
client/views/userProfile.html | 6 +-
7 files changed, 91 insertions(+), 57 deletions(-)
diff --git a/client/lib/globalhelpers.html b/client/lib/globalhelpers.html
index f73a587..ac6d081 100644
--- a/client/lib/globalhelpers.html
+++ b/client/lib/globalhelpers.html
@@ -1,3 +1,10 @@
{{username this}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/lib/globalhelpers.js b/client/lib/globalhelpers.js
index c96a32a..0ce9298 100644
--- a/client/lib/globalhelpers.js
+++ b/client/lib/globalhelpers.js
@@ -39,13 +39,13 @@ Handlebars.registerHelper('privacyIcon', function(playlist) {
return '';
});
-Template.userProfile.avatarUrl = function() {
- if(this.profile.avatar) {
- return this.profile.avatar;
+Handlebars.registerHelper('avatarUrl', function(user) {
+ if(user.profile.avatar) {
+ return user.profile.avatar;
} else {
return "/img/avatar.jpg";
}
-};
+});
Handlebars.registerHelper('currentRoute', function() {
var currentRoute = Router.current();
@@ -57,3 +57,8 @@ Handlebars.registerHelper('currentRoute', function() {
Handlebars.registerHelper('sessionIs', function(p1, p2) {
return Session.get(p1) === p2;
});
+
+
+Template.circleAvatar.rendered = function() {
+ $('div.avatar').tooltip();
+}
diff --git a/client/stylesheets/fusic.import.less b/client/stylesheets/fusic.import.less
index 18f8ec7..61216e9 100644
--- a/client/stylesheets/fusic.import.less
+++ b/client/stylesheets/fusic.import.less
@@ -1,3 +1,13 @@
+#playlistName:hover a#editToggle {
+ opacity:1;
+}
+a#editToggle {
+ font-size:0.5em;
+ opacity:0;
+ cursor:pointer;
+ transition: all 0.33s ease;
+}
+
img.avatar {
background-color:@gray-lighter;
width:300px;
diff --git a/client/views/playlist.css b/client/views/playlist.css
index b30ad76..2e52aef 100644
--- a/client/views/playlist.css
+++ b/client/views/playlist.css
@@ -10,6 +10,7 @@
div.inline {
display:inline-block;
}
+
.overlay-container {
position:relative;
}
@@ -22,6 +23,8 @@ div.inline {
padding: 4px;
}
+
+
.playlist-container:hover {
background-color: #eee;
}
@@ -59,6 +62,9 @@ li.youtube-result:hover {
background-color:#eee;
}
+
+
+
span[data-action*="play"] {
cursor:pointer;
}
diff --git a/client/views/playlists.html b/client/views/playlists.html
index 300dbb7..19aa819 100644
--- a/client/views/playlists.html
+++ b/client/views/playlists.html
@@ -69,10 +69,13 @@ All playlists
-
edit
-
+
+
+ {{{privacyIcon this}}}
+ {{title}}
+
+
-
{{{privacyIcon this}}} {{title}}
{{#if description}}
{{description}}
{{/if}}
@@ -84,52 +87,44 @@
{{{privacyIcon this}}} {{title}}
-
+
-
-
-
- {{#if playing}}
-
- {{else}}
-
- {{/if}}
- {{#if following}}
-
+
+
+ {{#if playing}}
+
- {{else}}
-
+
+
+
+
Followers
+ {{#each followers}}
+ {{>circleAvatar this}}
+ {{else}}
+
None yet
+ {{/each}}
-
-
Followers:
-
- {{#each followers}}
- -
- {{> userLink}}
-
- {{else}}
- - Nobody
- {{/each}}
-
-
-
Currently playing:
-
- {{#each players}}
- -
- {{> userLink}}
-
- {{/each}}
-
@@ -160,7 +155,7 @@
Stats
- {{> quickForm collection="Playlists" doc=editingDoc id="updatePlaylistForm" type="update" fields="title,cover,tags,description"}}
+ {{> quickForm collection="Playlists" doc=editingDoc id="updatePlaylistForm" type="update" fields="title,cover,tags,description" buttonContent="Update"}}
diff --git a/client/views/playlists.js b/client/views/playlists.js
index 5d5423e..96e7b45 100644
--- a/client/views/playlists.js
+++ b/client/views/playlists.js
@@ -19,15 +19,28 @@ Template.insertPlaylistForm.events = {
}
}
+Template.playlistsEntry.events = {
+ 'click .playlist-container' : function(e,template) {
+ Router.go('/playlist/'+this._id);
+ }
+}
+
Template.playlist.rendered = function() {
AutoForm.hooks({
removeButton: { //on successful remove, go back to playlist page
- onSuccess: function(operation, result, template) {
- Router.go('playlists');
- }
- }
- });
+ onSuccess: function(operation, result, template) {
+ Router.go('playlists');
+ }
+ },
+ updatePlaylistForm: { //on successful edit, collapse back out
+ onSuccess: function(operation, result, template) {
+ $('#playlistInfo').collapse('show');
+ $('#updatePlaylistForm').collapse('hide');
+
+ }
+ }
+ });
}
Template.playlist.selected = function () {
diff --git a/client/views/userProfile.html b/client/views/userProfile.html
index 69b5147..4625524 100644
--- a/client/views/userProfile.html
+++ b/client/views/userProfile.html
@@ -2,7 +2,7 @@
-
+
{{#if isOwner}}
@@ -16,9 +16,7 @@
Friends {{this.profile.friends.length}}
{{#each userFriends}}
-
-
-
+ {{>circleAvatar}}
{{/each}}