diff --git a/branding/client/branding.html b/branding/client/branding.html index ee90fbfc80..f0c1275ab9 100644 --- a/branding/client/branding.html +++ b/branding/client/branding.html @@ -12,8 +12,10 @@

{{# if branding }} - {{> viewProjectLogo branding=branding }} - {{> uploadProjectLogo branding=branding }} + {{> viewProjectLogo branding=branding }} + {{> uploadProjectLogo branding=branding }} + {{> viewCoverPhoto branding=branding }} + {{> uploadCoverPhoto branding=branding }} {{# autoForm id="brandingEdit" type="update" diff --git a/branding/collection/schema.js b/branding/collection/schema.js index d9d90e337e..cd1a0d279b 100644 --- a/branding/collection/schema.js +++ b/branding/collection/schema.js @@ -6,6 +6,10 @@ Branding.schema = new SimpleSchema({ type: String, optional: true, }, + coverPhotoFileId: { + type: String, + optional: true, + }, colors: { type: Object, optional: true, diff --git a/branding/collection/server/publications.js b/branding/collection/server/publications.js index a4f9f4dc7d..8c18898043 100644 --- a/branding/collection/server/publications.js +++ b/branding/collection/server/publications.js @@ -1,6 +1,9 @@ +// Meteor package import +import { Meteor } from 'meteor/meteor'; +// Apinf collections import import { Branding } from '/branding/collection'; Meteor.publish('branding', function() { // Get Branding collection object - return Branding.find({}); + return Branding.find(); }); diff --git a/branding/cover_photo/client/upload/resumable.js b/branding/cover_photo/client/upload/resumable.js new file mode 100644 index 0000000000..56d0429033 --- /dev/null +++ b/branding/cover_photo/client/upload/resumable.js @@ -0,0 +1,60 @@ +// Import meteor packages +import { Meteor } from 'meteor/meteor'; +import { sAlert } from 'meteor/juliancwirko:s-alert'; +import { TAPi18n } from 'meteor/tap:i18n'; + +// Import apinf collections +import { Branding } from '/branding/collection'; +import CoverPhoto from '/branding/cover_photo/collection'; +import { fileNameEndsWith } from '/core/helper_functions/file_name_ends_with'; + +Meteor.startup(function () { + // Set cover photo id to branding collection on Success + CoverPhoto.resumable.on('fileSuccess', function (file) { + + // Get the id from project logo file object + const coverPhotoFileId = file.uniqueIdentifier; + + // Get branding + const branding = Branding.findOne(); + + // Update logo id field + Branding.update(branding._id, { $set: { coverPhotoFileId } }); + + // Get upload success message translation + const message = TAPi18n.__('uploadCoverPhoto_successfully_uploaded'); + + // Alert user of successful upload + sAlert.success(message); + }); + + CoverPhoto.resumable.on('fileAdded', function (file) { + return CoverPhoto.insert({ + _id: file.uniqueIdentifier, + filename: file.fileName, + contentType: file.file.type, + }, function (err) { + if (err) { + // Create & show a message about failed + const message = `${TAPi18n.__('uploadCoverPhoto_acceptedExtensions_errorText')} ${err}`; + sAlert.warning(message) + return; + } + + // Available extensions for pictures + const acceptedExtensions = ['jpg', 'jpeg', 'png', 'gif']; + + // Check extensions for uploading file: is it a picture or not? + if (fileNameEndsWith(file.file.name, acceptedExtensions)) { + // Upload the cover photo + return CoverPhoto.resumable.upload(); + } + + // Get extension error message + const message = TAPi18n.__('uploadCoverPhoto_acceptedExtensions'); + + // Alert user of extension error + sAlert.error(message); + }); + }); +}); diff --git a/branding/cover_photo/client/upload/upload.css b/branding/cover_photo/client/upload/upload.css new file mode 100644 index 0000000000..d9248804f5 --- /dev/null +++ b/branding/cover_photo/client/upload/upload.css @@ -0,0 +1,3 @@ +.cover-photo-file { + margin-bottom: 1em; +} diff --git a/branding/cover_photo/client/upload/upload.html b/branding/cover_photo/client/upload/upload.html new file mode 100644 index 0000000000..a24e32f58f --- /dev/null +++ b/branding/cover_photo/client/upload/upload.html @@ -0,0 +1,19 @@ + diff --git a/branding/cover_photo/client/upload/upload.js b/branding/cover_photo/client/upload/upload.js new file mode 100644 index 0000000000..a3774108bb --- /dev/null +++ b/branding/cover_photo/client/upload/upload.js @@ -0,0 +1,59 @@ +// Import meteor packages +import { Template } from 'meteor/templating'; +import { Mongo } from 'meteor/mongo'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { TAPi18n } from 'meteor/tap:i18n'; +import { sAlert } from 'meteor/juliancwirko:s-alert'; +// Import apinf collections +import { Branding } from '/branding/collection'; +import CoverPhoto from '/branding/cover_photo/collection'; + +Template.uploadCoverPhoto.onCreated(function () { + const instance = this; + + // Subscribe to Branding collection + instance.subscribe('branding'); + // Subscribe to Cover Photo collection + instance.subscribe('coverPhoto'); +}); + +Template.uploadCoverPhoto.helpers({ + uploadedCoverPhotoFile () { + // Get cover photo ID + const currentCoverPhotoFileId = this.branding.coverPhotoFileId; + + // Convert to Mongo ObjectID + const objectId = new Mongo.Collection.ObjectID(currentCoverPhotoFileId); + + // Check if cover photo file is available + return CoverPhoto.findOne(objectId); + }, +}); + +Template.uploadCoverPhoto.events({ + 'click .delete-cover-photo': function () { + // Show confirmation dialog to user + const confirmation = confirm(TAPi18n.__('uploadCoverPhoto_confirm_delete')); + + // Check if user clicked "OK" + if (confirmation) { + // Get cover photo file id from branding + const coverPhotoFileId = this.branding.coverPhotoFileId; + + // Convert to Mongo ObjectID + const objectId = new Mongo.Collection.ObjectID(coverPhotoFileId); + + // Remove the cover photo object + CoverPhoto.remove(objectId); + + // Remove the cover photo file id field + Branding.update(this.branding._id, { $unset: { coverPhotoFileId: '' } }); + + // Get deletion success message translation + const message = TAPi18n.__('uploadCoverPhoto_successfully_deleted'); + + // Alert user of successful delete + sAlert.success(message); + } + }, +}); diff --git a/branding/cover_photo/client/upload/uploadButton/uploadButton.html b/branding/cover_photo/client/upload/uploadButton/uploadButton.html new file mode 100644 index 0000000000..9630b913d0 --- /dev/null +++ b/branding/cover_photo/client/upload/uploadButton/uploadButton.html @@ -0,0 +1,5 @@ + diff --git a/branding/cover_photo/client/upload/uploadButton/uploadButton.js b/branding/cover_photo/client/upload/uploadButton/uploadButton.js new file mode 100644 index 0000000000..ee49f6d31a --- /dev/null +++ b/branding/cover_photo/client/upload/uploadButton/uploadButton.js @@ -0,0 +1,9 @@ +// Import meteor packages +import { Template } from 'meteor/templating'; +// Import apinf collections +import CoverPhoto from '/branding/cover_photo/collection'; + +Template.uploadCoverPhotoButton.onRendered(function() { + // Assign resumable browse to element + CoverPhoto.resumable.assignBrowse($('#cover-photo-browse')); +}); diff --git a/branding/cover_photo/client/view/view.css b/branding/cover_photo/client/view/view.css new file mode 100644 index 0000000000..8acb8525e0 --- /dev/null +++ b/branding/cover_photo/client/view/view.css @@ -0,0 +1,4 @@ +.view-cover-photo { + width: 140px; + margin-bottom: 1em; +} diff --git a/branding/cover_photo/client/view/view.html b/branding/cover_photo/client/view/view.html new file mode 100644 index 0000000000..42681e8720 --- /dev/null +++ b/branding/cover_photo/client/view/view.html @@ -0,0 +1,5 @@ + diff --git a/branding/cover_photo/client/view/view.js b/branding/cover_photo/client/view/view.js new file mode 100644 index 0000000000..67523375ef --- /dev/null +++ b/branding/cover_photo/client/view/view.js @@ -0,0 +1,41 @@ +// Import meteor packages +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import { Template } from 'meteor/templating'; +// Import apinf collections +import { Branding } from '/branding/collection'; +import CoverPhoto from '/branding/cover_photo/collection'; + +Template.viewCoverPhoto.onCreated(function () { + const instance = this; + // Subscribe to Branding collection + instance.subscribe('branding'); + // Subscribe to Cover Photo collection + instance.subscribe('coverPhoto'); +}); + +Template.viewCoverPhoto.helpers({ + coverPhotoExists () { + // Get Branding collection + const branding = Branding.findOne(); + + // Check Branding collection and cover photo exist + return branding && branding.coverPhotoFileId; + }, + uploadedCoverPhotoLink () { + const currentCoverPhotoFileId = Branding.findOne().coverPhotoFileId; + + // Convert to Mongo ObjectID + const objectId = new Mongo.Collection.ObjectID(currentCoverPhotoFileId); + + // Get cover photo file Object + const currentCoverPhotoFile = CoverPhoto.findOne(objectId); + + // Check if cover photo file is available + if (currentCoverPhotoFile) { + // Get cover photo file URL + return Meteor.absoluteUrl().slice(0, -1) + CoverPhoto.baseURL + '/md5/' + currentCoverPhotoFile.md5; + } + return ''; + }, +}); diff --git a/branding/cover_photo/collection/index.js b/branding/cover_photo/collection/index.js new file mode 100644 index 0000000000..0acf21bb78 --- /dev/null +++ b/branding/cover_photo/collection/index.js @@ -0,0 +1,19 @@ +// Import Meteor packages +import { FileCollection } from 'meteor/vsivsi:file-collection'; + +const CoverPhoto = new FileCollection('CoverPhoto', { + resumable: true, // Enable built-in resumable.js upload support + http: [ + { method: 'get', + // this will be at route "/gridfs/CoverPhoto/md5/:md5" + path: '/md5/:md5', + // uses express style url params + // a query mapping url to CoverPhoto + lookup: function (params) { + return { md5: params.md5 }; + }, + }, + ], +}); + +export default CoverPhoto; diff --git a/branding/cover_photo/collection/server/permissions.js b/branding/cover_photo/collection/server/permissions.js new file mode 100644 index 0000000000..ecc7a929ce --- /dev/null +++ b/branding/cover_photo/collection/server/permissions.js @@ -0,0 +1,19 @@ +// Import meteor package +import { Roles } from 'meteor/alanning:roles'; +// Import apinf collection +import CoverPhoto from '/branding/cover_photo/collection'; + +CoverPhoto.allow({ + insert: function (userId) { + return Roles.userIsInRole(userId, ['admin']); + }, + remove: function (userId) { + return Roles.userIsInRole(userId, ['admin']); + }, + read: function () { + return true; + }, + write: function () { + return true; + }, +}); diff --git a/branding/cover_photo/server/publications.js b/branding/cover_photo/server/publications.js new file mode 100644 index 0000000000..19839d40a4 --- /dev/null +++ b/branding/cover_photo/server/publications.js @@ -0,0 +1,12 @@ +// Import Meteor package +import { Meteor } from 'meteor/meteor'; +// Import apinf collection +import CoverPhoto from '/branding/cover_photo/collection'; + +Meteor.publish('coverPhoto', () => { + return CoverPhoto.find({ + 'metadata._Resumable': { + $exists: false, + }, + }); +}); diff --git a/branding/logo/upload/client/resumable.js b/branding/logo/client/upload/resumable.js similarity index 100% rename from branding/logo/upload/client/resumable.js rename to branding/logo/client/upload/resumable.js diff --git a/branding/logo/upload/client/upload.css b/branding/logo/client/upload/upload.css similarity index 100% rename from branding/logo/upload/client/upload.css rename to branding/logo/client/upload/upload.css diff --git a/branding/logo/upload/client/upload.html b/branding/logo/client/upload/upload.html similarity index 100% rename from branding/logo/upload/client/upload.html rename to branding/logo/client/upload/upload.html diff --git a/branding/logo/upload/client/upload.js b/branding/logo/client/upload/upload.js similarity index 100% rename from branding/logo/upload/client/upload.js rename to branding/logo/client/upload/upload.js diff --git a/branding/logo/upload/client/uploadButton/uploadButton.html b/branding/logo/client/upload/uploadButton/uploadButton.html similarity index 100% rename from branding/logo/upload/client/uploadButton/uploadButton.html rename to branding/logo/client/upload/uploadButton/uploadButton.html diff --git a/branding/logo/upload/client/uploadButton/uploadButton.js b/branding/logo/client/upload/uploadButton/uploadButton.js similarity index 100% rename from branding/logo/upload/client/uploadButton/uploadButton.js rename to branding/logo/client/upload/uploadButton/uploadButton.js diff --git a/branding/logo/view/client/view.css b/branding/logo/client/view/view.css similarity index 100% rename from branding/logo/view/client/view.css rename to branding/logo/client/view/view.css diff --git a/branding/logo/view/client/view.html b/branding/logo/client/view/view.html similarity index 100% rename from branding/logo/view/client/view.html rename to branding/logo/client/view/view.html diff --git a/branding/logo/view/client/view.js b/branding/logo/client/view/view.js similarity index 100% rename from branding/logo/view/client/view.js rename to branding/logo/client/view/view.js diff --git a/core/lib/i18n/en.i18n.json b/core/lib/i18n/en.i18n.json index b454247c04..040abb87bd 100644 --- a/core/lib/i18n/en.i18n.json +++ b/core/lib/i18n/en.i18n.json @@ -585,10 +585,17 @@ "uploadApiLogoButton": "Upload logo", "uploadApiLogo_confirm_delete": "Are you sure you want to delete this logo?", "uploadApiLogo_successfully_deleted": "Logo successfully deleted!", + "uploadCoverPhoto_confirm_delete": "Cover photo area will show primary branding color after photo has been deleted. The image will be removed from the collection.", + "uploadCoverPhoto_acceptedExtensions": "Only .jpg, .jpeg, .png and .gif file formats are allowed.", + "uploadCoverPhoto_acceptedExtensions_errorText": "File creation failed!", + "uploadCoverPhoto_successfully_uploaded": "Your cover photo was successfully uploaded!", + "uploadCoverPhoto_successfully_deleted": "Your cover photo was successfully deleted!", + "uploadCoverPhotoButton_textButton": "Upload cover photo", "uploadProjectLogo_confirm_delete": "Are you sure you want to delete your project logo?", "uploadProjectLogo_acceptedExtensions": "Only .jpg, .jpeg, .png and .gif file formats are allowed.", "uploadProjectLogo_successfully_uploaded": "Your project logo successfully uploaded!", "uploadProjectLogo_successfully_deleted": "Your project logo successfully deleted!", + "uploadProjectLogoButton_textButton": "Upload logo", "userMenu_account": "Account", "userMenu_branding": "Branding", "userMenu_profile": "Profile", diff --git a/home/client/body/homeBody.html b/home/client/body/homeBody.html index 72e6c2183c..73c79109af 100755 --- a/home/client/body/homeBody.html +++ b/home/client/body/homeBody.html @@ -1,5 +1,5 @@