mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
cover photo for groups, #2588
This commit is contained in:
@@ -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;
|
||||
});
|
||||
157
public/vendor/jquery/draggable-background/backgroundDraggable.js
vendored
Normal file
157
public/vendor/jquery/draggable-background/backgroundDraggable.js
vendored
Normal file
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user