cover photo for groups, #2588

v1.18.x
Julian Lam 10 years ago
parent 697d2636f9
commit 675968f0f3

@ -5,6 +5,11 @@
"new_group": "Create New Group",
"no_groups_found": "There are no groups to see",
"cover-instructions": "Drag and Drop a photo, drag to position, and hit <strong>Save</strong>",
"cover-change": "Change",
"cover-save": "Save",
"cover-saving": "Saving",
"details.title": "Group Details",
"details.members": "Member List",
"details.pending": "Pending Members",

@ -1,14 +1,17 @@
"use strict";
/* globals define, socket, ajaxify, app, bootbox */
/* globals define, socket, ajaxify, app, bootbox, RELATIVE_PATH */
define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker'], function(iconSelect) {
var Details = {};
define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker', 'vendor/jquery/draggable-background/backgroundDraggable'], function(iconSelect) {
var Details = {
cover: {}
};
Details.init = function() {
var detailsPage = $('.groups'),
settingsFormEl = detailsPage.find('form');
Details.prepareSettings();
Details.initialiseCover();
$('.latest-posts .content img').addClass('img-responsive');
@ -150,5 +153,87 @@ define('forum/groups/details', ['iconSelect', 'vendor/colorpicker/colorpicker'],
});
};
Details.initialiseCover = function() {
var coverEl = $('.group-cover');
coverEl.find('.change').on('click', function() {
coverEl.toggleClass('active', 1);
coverEl.backgroundDraggable();
coverEl.on('dragover', Details.cover.onDragOver);
coverEl.on('drop', Details.cover.onDrop);
});
coverEl.find('.save').on('click', Details.cover.save);
};
// Cover Photo Handling Code
Details.cover.load = function() {
socket.emit('groups.cover.get', function(err, data) {
if (!err) {
var coverEl = $('.group-cover');
if (data['cover:url']) {
coverEl.css('background-image', 'url(' + RELATIVE_PATH + '/theme-rocket/cover/' + data['cover:url'] + ')');
}
if (data['cover:position']) {
coverEl.css('background-position', data['cover:position']);
}
delete Details.cover.newCover;
} else {
app.alertError(err.message);
}
});
};
Details.cover.onDragOver = function(e) {
e.stopPropagation();
e.preventDefault();
e.originalEvent.dataTransfer.dropEffect = 'copy';
};
Details.cover.onDrop = function(e) {
var coverEl = $('.group-cover');
e.stopPropagation();
e.preventDefault();
var files = e.originalEvent.dataTransfer.files,
reader = new FileReader();
if (files.length && files[0].type.match('image.*')) {
reader.onload = function(e) {
coverEl.css('background-image', 'url(' + e.target.result + ')');
coverEl.backgroundDraggable();
Details.cover.newCover = e.target.result;
};
reader.readAsDataURL(files[0]);
}
};
Details.cover.save = function() {
var coverEl = $('.group-cover');
coverEl.addClass('saving');
socket.emit('groups.cover.update', {
groupName: ajaxify.variables.get('group_name'),
imageData: Details.cover.newCover || undefined,
position: $('.group-cover').css('background-position')
}, function(err) {
if (!err) {
coverEl.toggleClass('active', 0);
coverEl.backgroundDraggable('disable');
coverEl.off('dragover', Details.cover.onDragOver);
coverEl.off('drop', Details.cover.onDrop);
Details.cover.load();
} else {
app.alertError(err.message);
}
coverEl.removeClass('saving');
});
};
return Details;
});

@ -0,0 +1,157 @@
/**
* Draggable Background plugin for jQuery
*
* v1.2.4
*
* Copyright (c) 2014 Kenneth Chung
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
;(function($) {
var $window = $(window);
// Helper function to guarantee a value between low and hi unless bool is false
var limit = function(low, hi, value, bool) {
if (arguments.length === 3 || bool) {
if (value < low) return low;
if (value > hi) return hi;
}
return value;
};
// Adds clientX and clientY properties to the jQuery's event object from touch
var modifyEventForTouch = function(e) {
e.clientX = e.originalEvent.touches[0].clientX;
e.clientY = e.originalEvent.touches[0].clientY;
};
var getBackgroundImageDimensions = function($el) {
var bgSrc = ($el.css('background-image').match(/^url\(['"]?(.*?)['"]?\)$/i) || [])[1];
if (!bgSrc) return;
var imageDimensions = { width: 0, height: 0 },
image = new Image();
image.onload = function() {
if ($el.css('background-size') == "cover") {
var elementWidth = $el.innerWidth(),
elementHeight = $el.innerHeight(),
elementAspectRatio = elementWidth / elementHeight;
imageAspectRatio = image.width / image.height,
scale = 1;
if (imageAspectRatio >= elementAspectRatio) {
scale = elementHeight / image.height;
} else {
scale = elementWidth / image.width;
}
imageDimensions.width = image.width * scale;
imageDimensions.height = image.height * scale;
} else {
imageDimensions.width = image.width;
imageDimensions.height = image.height;
}
};
image.src = bgSrc;
return imageDimensions;
};
function Plugin(element, options) {
this.element = element;
this.options = options;
this.init();
}
Plugin.prototype.init = function() {
var $el = $(this.element),
bgSrc = ($el.css('background-image').match(/^url\(['"]?(.*?)['"]?\)$/i) || [])[1],
options = this.options;
if (!bgSrc) return;
// Get the image's width and height if bound
var imageDimensions = { width: 0, height: 0 };
if (options.bound) {
imageDimensions = getBackgroundImageDimensions($el);
}
$el.on('mousedown.dbg touchstart.dbg', function(e) {
if (e.target !== $el[0]) {
return;
}
e.preventDefault();
if (e.originalEvent.touches) {
modifyEventForTouch(e);
} else if (e.which !== 1) {
return;
}
var x0 = e.clientX,
y0 = e.clientY,
pos = $el.css('background-position').match(/(-?\d+).*?\s(-?\d+)/) || [],
xPos = parseInt(pos[1]) || 0,
yPos = parseInt(pos[2]) || 0;
$window.on('mousemove.dbg touchmove.dbg', function(e) {
e.preventDefault();
if (e.originalEvent.touches) {
modifyEventForTouch(e);
}
var x = e.clientX,
y = e.clientY;
xPos = options.axis === 'y' ? xPos : limit($el.innerWidth()-imageDimensions.width, 0, xPos+x-x0, options.bound);
yPos = options.axis === 'x' ? yPos : limit($el.innerHeight()-imageDimensions.height, 0, yPos+y-y0, options.bound);
x0 = x;
y0 = y;
$el.css('background-position', xPos + 'px ' + yPos + 'px');
});
$window.on('mouseup.dbg touchend.dbg mouseleave.dbg', function() {
if (options.done) {
options.done();
}
$window.off('mousemove.dbg touchmove.dbg');
$window.off('mouseup.dbg touchend.dbg mouseleave.dbg');
});
});
};
Plugin.prototype.disable = function() {
var $el = $(this.element);
$el.off('mousedown.dbg touchstart.dbg');
$window.off('mousemove.dbg touchmove.dbg mouseup.dbg touchend.dbg mouseleave.dbg');
}
$.fn.backgroundDraggable = function(options) {
var options = options;
var args = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var $this = $(this);
if (typeof options == 'undefined' || typeof options == 'object') {
options = $.extend({}, $.fn.backgroundDraggable.defaults, options);
var plugin = new Plugin(this, options);
$this.data('dbg', plugin);
} else if (typeof options == 'string' && $this.data('dbg')) {
var plugin = $this.data('dbg');
Plugin.prototype[options].apply(plugin, args);
}
});
};
$.fn.backgroundDraggable.defaults = {
bound: true,
axis: undefined
};
}(jQuery));

@ -72,6 +72,10 @@ uploadsController.uploadThumb = function(req, res, next) {
}, next);
};
uploadsController.uploadGroupCover = function(data, next) {
uploadImage(0/*req.user.uid*/, data, next);
};
function uploadImage(uid, image, callback) {
if (plugins.hasListeners('filter:uploadImage')) {
return plugins.fireHook('filter:uploadImage', {image: image, uid: uid}, callback);

@ -3,14 +3,20 @@
var async = require('async'),
winston = require('winston'),
_ = require('underscore'),
crypto = require('crypto'),
path = require('path'),
nconf = require('nconf'),
fs = require('fs'),
user = require('./user'),
meta = require('./meta'),
db = require('./database'),
plugins = require('./plugins'),
posts = require('./posts'),
privileges = require('./privileges'),
utils = require('../public/src/utils');
utils = require('../public/src/utils'),
uploadsController = require('./controllers/uploads');
(function(Groups) {
@ -60,7 +66,12 @@ var async = require('async'),
}
return groups;
}
}/*,
fixImageUrl: function(url) {
if (url) {
return url.indexOf('http') === -1 ? nconf.get('relative_path') + url : url;
}
}*/
};
Groups.list = function(options, callback) {
@ -185,6 +196,7 @@ var async = require('async'),
return callback(err);
}
// results.base.image = internals.fixImageUrl(results.base.image);
results.base.members = results.users.filter(Boolean);
results.base.pending = results.pending.filter(Boolean);
results.base.count = numUsers || results.base.members.length;
@ -203,6 +215,15 @@ var async = require('async'),
});
};
Groups.getGroupFields = function(groupName, fields, callback) {
db.getObjectFields('group:' + groupName, fields, callback);
};
Groups.setGroupField = function(groupName, field, value, callback) {
plugins.fireHook('action:group.set', {field: field, value: value, type: 'set'});
db.setObjectField('group:' + groupName, field, value, callback);
};
Groups.isPrivate = function(groupName, callback) {
db.getObjectField('group:' + groupName, 'private', function(err, isPrivate) {
isPrivate = isPrivate || isPrivate === null;
@ -688,6 +709,63 @@ var async = require('async'),
});
};
Groups.updateCoverPosition = function(groupName, position, callback) {
Groups.setGroupField(groupName, 'cover:position', position, callback);
};
Groups.updateCover = function(data, callback) {
var tempPath, md5sum, url;
// Position only? That's fine
if (!data.imageData && data.position) {
return Groups.updateCoverPosition(data.groupName, data.position, callback);
}
async.series([
function(next) {
// Calculate md5sum of image
// This is required because user data can be private
md5sum = crypto.createHash('md5');
md5sum.update(data.imageData);
md5sum = md5sum.digest('hex');
next();
},
function(next) {
// Save image
tempPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), md5sum);
var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64');
fs.writeFile(tempPath, buffer, {
encoding: 'base64'
}, next);
},
function(next) {
uploadsController.uploadGroupCover({
path: tempPath
}, function(err, uploadData) {
if (err) {
return next(err);
}
url = uploadData.url;
next();
});
},
function(next) {
Groups.setGroupField(data.groupName, 'cover:url', url, next);
},
function(next) {
fs.unlink(tempPath, next); // Delete temporary file
}
], function(err) {
if (err) {
return callback(err);
}
Groups.updateCoverPosition(data.groupName, data.position, callback);
});
}
Groups.ownership = {};
Groups.ownership.isOwner = function(uid, groupName, callback) {

@ -126,4 +126,26 @@ SocketGroups.delete = function(socket, data, callback) {
});
};
SocketGroups.cover = {};
SocketGroups.cover.get = function(socket, data, callback) {
groups.getGroupFields(data.groupName, ['cover:url', 'cover:position'], callback);
};
SocketGroups.cover.update = function(socket, data, callback) {
if(!data) {
return callback(new Error('[[error:invalid-data]]'));
} else if (socket.uid === 0) {
return callback(new Error('[[error:no-privileges]]'));
}
groups.ownership.isOwner(socket.uid, data.groupName, function(err, isOwner) {
if (!isOwner) {
return callback(new Error('[[error:no-privileges]]'));
}
groups.updateCover(data, callback);
});
};
module.exports = SocketGroups;

Loading…
Cancel
Save