diff --git a/.gitignore b/.gitignore index d2ce22da3..6c9c708d9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ configs/version.js slidewiki migrate .idea/ +package-lock.json diff --git a/.gitmodules b/.gitmodules index be4a50be6..3ff1268e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "custom_modules/custom-semantic-ui"] path = custom_modules/custom-semantic-ui url = https://github.com/slidewiki/custom-semantic-ui.git -[submodule "custom_modules/simple-draggable"] - path = custom_modules/simple-draggable - url = https://github.com/slidewiki/simple-draggable.git [submodule "custom_modules/reveal.js"] path = custom_modules/reveal.js - url = https://github.com/slidewiki/reveal.js + url = https://github.com/slidewiki/reveal.js.git diff --git a/.travis.yml b/.travis.yml index 70de3f8ef..1178e42db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js -node_js: 6 +node_js: 8 sudo: required git: depth: 5 @@ -23,6 +23,7 @@ after_success: - openssl aes-256-cbc -K $encrypted_ecdba13b2a41_key -iv $encrypted_ecdba13b2a41_iv -in deployment_keys.tar.enc -out deployment_keys.tar -d - if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] ; then ./travis_scripts/dockerhub.sh ; fi - if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] ; then ./travis_scripts/deploy.sh ; fi +- npm run coverall after_script: - rm -f deployment_keys.tar - rm -f ~/.docker/{cert.pem,key.pem,ca.pem,config.json} diff --git a/Dockerfile b/Dockerfile index a82d2094d..af80540d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM slidewiki/runtime:latest +FROM slidewiki/runtime:nodejs-8-slim MAINTAINER Ali Khalili "hyperir@gmail.com" ARG BUILD_ENV=local diff --git a/actions/activityfeed/addActivities.js b/actions/activityfeed/addActivities.js index 58d66bf69..16405b39f 100644 --- a/actions/activityfeed/addActivities.js +++ b/actions/activityfeed/addActivities.js @@ -1,8 +1,13 @@ +import UserProfileStore from '../../stores/UserProfileStore'; import serviceUnavailable from '../error/serviceUnavailable'; const log = require('../log/clog'); export default function addActivities(context, payload, done) { log.info(context); + + //enrich with jwt + payload.jwt = context.getStore(UserProfileStore).jwt; + context.service.create('activities.newarray', payload, {timeout: 20 * 1000}, (err, res) => { if (err) { log.error(context, {filepath: __filename}); diff --git a/actions/activityfeed/addActivity.js b/actions/activityfeed/addActivity.js index 6290ffcb6..ed4f76092 100644 --- a/actions/activityfeed/addActivity.js +++ b/actions/activityfeed/addActivity.js @@ -1,8 +1,13 @@ +import UserProfileStore from '../../stores/UserProfileStore'; import serviceUnavailable from '../error/serviceUnavailable'; const log = require('../log/clog'); export default function addActivity(context, payload, done) { log.info(context); + + //enrich with jwt + payload.jwt = context.getStore(UserProfileStore).jwt; + context.service.create('activities.new', payload, {timeout: 20 * 1000}, (err, res) => { if (err) { log.error(context, {filepath: __filename}); diff --git a/actions/activityfeed/likeActivity.js b/actions/activityfeed/likeActivity.js index 941ef35dd..97548837d 100644 --- a/actions/activityfeed/likeActivity.js +++ b/actions/activityfeed/likeActivity.js @@ -1,8 +1,13 @@ const log = require('../log/clog'); +import UserProfileStore from '../../stores/UserProfileStore'; import serviceUnavailable from '../error/serviceUnavailable'; export default function likeActivity(context, payload, done) { log.info(context); + + //enrich with jwt + payload.jwt = context.getStore(UserProfileStore).jwt; + context.service.create('like.likeActivity', payload, {}, {timeout: 20 * 1000}, (err, res) => { if (err) { log.error(context, {filepath: __filename}); diff --git a/actions/activityfeed/loadActivities.js b/actions/activityfeed/loadActivities.js index 11877d60f..c58bf0dc1 100644 --- a/actions/activityfeed/loadActivities.js +++ b/actions/activityfeed/loadActivities.js @@ -30,6 +30,16 @@ export default function loadActivities(context, payload, done) { // context.dispatch('UPDATE_PAGE_TITLE', { // pageTitle: pageTitle // }); - done(); + if (payload.params.stype !== 'deck') + return done(); + context.service.read('presentation.live', {id: payload.params.sid}, {timeout: 20 * 1000}, (err, res) => { + if (err) { + log.error(context, {filepath: __filename}); + context.executeAction(serviceUnavailable, payload, done); + } else { + context.dispatch('LOAD_PRESENTATIONS_SUCCESS', res); + } + done(); + }); }); } diff --git a/actions/decktree/moveTreeNode.js b/actions/decktree/moveTreeNode.js index 5adafb273..297ea8c88 100644 --- a/actions/decktree/moveTreeNode.js +++ b/actions/decktree/moveTreeNode.js @@ -1,4 +1,5 @@ import UserProfileStore from '../../stores/UserProfileStore'; +import addActivity from '../activityfeed/addActivity'; const log = require('../log/clog'); export default function moveTreeNode(context, payload, done) { @@ -42,6 +43,15 @@ export default function moveTreeNode(context, payload, done) { context.dispatch('MOVE_TREE_NODE_FAILURE', err); } else { context.dispatch('MOVE_TREE_NODE_SUCCESS', payload); + + let activity = { + activity_type: 'move', + user_id: String(userid), + content_name: res.title, + content_id: String(res.id), + content_kind: res.type + }; + context.executeAction(addActivity, {activity: activity}); } done(null, res); }); diff --git a/actions/loadSupportedLanguages.js b/actions/loadSupportedLanguages.js new file mode 100644 index 000000000..13f1c0427 --- /dev/null +++ b/actions/loadSupportedLanguages.js @@ -0,0 +1,17 @@ +import serviceUnavailable from './error/serviceUnavailable'; +const log = require('./log/clog'); + +export default function loadSupportedLanguages(context, payload, done) { + log.info(context); + context.service.read('translation.supported', payload, {timeout: 20 * 1000}, (err, res) => { + // console.log('Executing loadPresentation action'); + if (err) { + log.error(context, {filepath: __filename, err: err}); + context.executeAction(serviceUnavailable, payload, done); + //context.dispatch('LOAD_FEATURED_FAILURE', err); + } else { + context.dispatch('LOAD_SUPPORTED_LANGS_SUCCESS', res); + } + done(); + }); +} diff --git a/actions/loadTranslations.js b/actions/loadTranslations.js index 0c3983db8..e829860ec 100644 --- a/actions/loadTranslations.js +++ b/actions/loadTranslations.js @@ -7,28 +7,20 @@ const log = require('./log/clog'); export default function loadTranslations(context, payload, done) { log.info(context); - if(!(['deck', 'slide', 'question'].indexOf(payload.params.stype) > -1 || payload.params.stype === undefined)) { - context.executeAction(deckContentTypeError, payload, done); - return; + if(!(['deck'].indexOf(payload.params.stype) > -1 || payload.params.stype === undefined)) { //this is slide + done(); + }else{ + context.service.read('translation.list', payload, {timeout: 20 * 1000}, (err, res) => { + if (err) { + log.error(context, {filepath: __filename}); + context.executeAction(serviceUnavailable, payload, done); + context.dispatch('LOAD_TRANSLATIONS_FAILURE', err); + } else { + context.dispatch('LOAD_TRANSLATIONS_SUCCESS', res); + } + done(); + }); } - if (!(AllowedPattern.SLIDE_ID.test(payload.params.sid) || payload.params.sid === undefined)) { - context.executeAction(slideIdTypeError, payload, done); - return; - } - context.service.read('translation.list', payload, {timeout: 20 * 1000}, (err, res) => { - if (err) { - log.error(context, {filepath: __filename}); - context.executeAction(serviceUnavailable, payload, done); - context.dispatch('LOAD_TRANSLATIONS_FAILURE', err); - } else { - context.dispatch('LOAD_TRANSLATIONS_SUCCESS', res); - } - let pageTitle = shortTitle + ' | Translations | ' + payload.params.stype + ' | ' + payload.params.sid; - context.dispatch('UPDATE_PAGE_TITLE', { - pageTitle: pageTitle - }); - done(); - }); } diff --git a/actions/media/uploadMediaFile.js b/actions/media/uploadMediaFile.js new file mode 100644 index 000000000..78c3c0b36 --- /dev/null +++ b/actions/media/uploadMediaFile.js @@ -0,0 +1,43 @@ +import UserProfileStore from '../../stores/UserProfileStore'; +import {Microservices} from '../../configs/microservices'; +const log = require('../log/clog'); + +export default function uploadMediaFile(context, payload, done) { + log.info(context); + + payload.userid = context.getStore(UserProfileStore).userid; + payload.jwt = context.getStore(UserProfileStore).jwt; + + context.dispatch('START_UPLOADING_MEDIA_FILE', {type: payload.type, name: payload.title}); + + context.service.create('media.create', payload, { timeout: 20 * 1000 }, { timeout: 20 * 1000 }, (err, res) => { + delete payload.jwt; + delete payload.userid; + + if (err) { + // Every file send to the file-service gets checked if its distinct, if so 409 is returned + // All images of all users are regarded thus the 409 response is really common + if (err.statusCode === 409) { + let parts = err.message.split(' '); + let filename = parts[parts.length-1]; + filename = filename.substring(0, filename.length - 4); + payload.url = Microservices.file.uri + '/picture/' + filename; + + let thumbnailName = filename.substring(0, filename.lastIndexOf('.')) + '_thumbnail' + filename.substr(filename.lastIndexOf('.')); + payload.thumbnailUrl = Microservices.file.uri + '/picture/' + thumbnailName; + + console.log('Got 409 from file service', payload); + context.dispatch('SUCCESS_UPLOADING_MEDIA_FILE', payload); + } + else { + context.dispatch('FAILURE_UPLOADING_MEDIA_FILE', err); + } + } + else { + payload.url = Microservices.file.uri + '/picture/' + res.fileName; + payload.thumbnailUrl = Microservices.file.uri + '/picture/' + res.thumbnailName; + context.dispatch('SUCCESS_UPLOADING_MEDIA_FILE', payload); + } + done(); + }); +} diff --git a/actions/media/uploadMediaFiles.js b/actions/media/uploadMediaFiles.js new file mode 100644 index 000000000..d8525658d --- /dev/null +++ b/actions/media/uploadMediaFiles.js @@ -0,0 +1,44 @@ +import UserProfileStore from '../../stores/UserProfileStore'; +import {Microservices} from '../../configs/microservices'; +const log = require('../log/clog'); + +export default function uploadMediaFiles(context, payload, done) { + log.info(context); + + payload.userid = context.getStore(UserProfileStore).userid; + payload.jwt = context.getStore(UserProfileStore).jwt; + + context.dispatch('START_UPLOADING_MEDIA_FILE', {type: payload.type, name: payload.title}); + + context.service.create('media.create', payload, { timeout: 20 * 1000 }, { timeout: 20 * 1000 }, (err, res) => { + if (err) { + // Every file send to the file-service gets checked if its distinct, if so 409 is returned + // All images of all users are regarded thus the 409 response is really common + if (err.statusCode === 409) { + let parts = err.message.split(' '); + let filename = parts[parts.length-1]; + filename = filename.substring(0, filename.length - 4); + payload.url = Microservices.file.uri + '/picture/' + filename; + + let thumbnailName = filename.substring(0, filename.lastIndexOf('.')) + '_thumbnail' + filename.substr(filename.lastIndexOf('.')); + payload.thumbnailUrl = Microservices.file.uri + '/picture/' + thumbnailName; + + delete payload.jwt; + delete payload.userid; + + console.log('Got 409 from file service', payload); + context.dispatch('SUCCESS_UPLOADING_MEDIA_FILE', payload); + } + else { + context.dispatch('FAILURE_UPLOADING_MEDIA_FILE', err); + } + } + else { + payload.url = Microservices.file.uri + '/picture/' + res.fileName; + payload.thumbnailUrl = Microservices.file.uri + '/picture/' + res.thumbnailName; + context.dispatch('SUCCESS_UPLOADING_MEDIA_FILE', payload); + + } + done(); + }); +} diff --git a/actions/media/uploadProfilePicture.js b/actions/media/uploadProfilePicture.js new file mode 100644 index 000000000..e2f975314 --- /dev/null +++ b/actions/media/uploadProfilePicture.js @@ -0,0 +1,29 @@ +import UserProfileStore from '../../stores/UserProfileStore'; +import {Microservices} from '../../configs/microservices'; +const log = require('../log/clog'); + +export default function uploadProfilePicture(context, payload, done) { + log.info(context); + + payload.userid = context.getStore(UserProfileStore).userid; + payload.jwt = context.getStore(UserProfileStore).jwt; + payload.username = context.getStore(UserProfileStore).username; + + // console.log('uploadProfilePicture', payload); + context.dispatch('START_UPLOADING_MEDIA_FILE', {type: payload.type, name: 'profile picture'}); + + context.service.create('media.uploadProfilePicture', payload, { timeout: 20 * 1000 }, { timeout: 20 * 1000 }, (err, res) => { + delete payload.jwt; + delete payload.userid; + delete payload.username; + + if (err) { + context.dispatch('FAILURE_UPLOADING_MEDIA_FILE', err); + } + else { + payload.url = res; + context.dispatch('SUCCESS_UPLOADING_MEDIA_FILE', payload); + } + done(); + }); +} diff --git a/actions/report/closeReportModal.js b/actions/report/closeReportModal.js index aa90dcb33..19b5fbeeb 100644 --- a/actions/report/closeReportModal.js +++ b/actions/report/closeReportModal.js @@ -1,8 +1,4 @@ -/** - * Created by lfernandes on 24.03.17. - */ - -const log = require('../log/clog'); +import log from '../log/clog'; export default function closeReportModal(context, payload, done) { log.info(context); diff --git a/actions/report/openReportModal.js b/actions/report/openReportModal.js index a2d0cf6a7..cc31d6040 100644 --- a/actions/report/openReportModal.js +++ b/actions/report/openReportModal.js @@ -1,8 +1,4 @@ -/** - * Created by lfernandes on 24.03.17. - */ - -const log = require('../log/clog'); +import log from '../log/clog'; export default function openReportModal(context, payload, done) { log.info(context); diff --git a/actions/report/sendReport.js b/actions/report/sendReport.js index 92eb8f619..f378e86b5 100644 --- a/actions/report/sendReport.js +++ b/actions/report/sendReport.js @@ -1,4 +1,34 @@ -/** - * Created by lfernandes on 12.03.17. - */ +import log from '../log/clog'; +export default function sendReport(context,payload,done){ + log.info(context); + + context.service.create('email', {subject: payload.subject, message: payload.text}, { timeout: 20 * 1000 }, (err, res) => { + + if (err) { + swal({ + title: payload.swal_messages.title, + text: payload.swal_messages.error_text, + type: 'error', + confirmButtonText: payload.swal_messages.error_confirmButtonText, + confirmButtonClass: 'positive ui button', + allowEscapeKey: false, + allowOutsideClick: false, + buttonsStyling: false + }); + + } else { + swal({ + title: payload.swal_messages.title, + text:payload.swal_messages.text, + type: 'success', + confirmButtonText: payload.swal_messages.confirmButtonText, + confirmButtonClass: 'positive ui button', + allowEscapeKey: false, + allowOutsideClick: false, + buttonsStyling: false + }); + } + done(); + }); +} diff --git a/actions/report/sendReportShowWrongFields.js b/actions/report/sendReportShowWrongFields.js index 84e94cfc5..eef329810 100644 --- a/actions/report/sendReportShowWrongFields.js +++ b/actions/report/sendReportShowWrongFields.js @@ -1,8 +1,4 @@ -/** - * Created by lfernandes on 12.03.17. - */ - -const log = require('../log/clog'); +import log from '../log/clog'; export default function SendReportShowWrongFields(context, payload, done) { log.info(context); diff --git a/actions/slide/loadSlideView.js b/actions/slide/loadSlideView.js index 758557d17..41ccdb1d6 100644 --- a/actions/slide/loadSlideView.js +++ b/actions/slide/loadSlideView.js @@ -10,6 +10,10 @@ export default function loadSlideView(context, payload, done) { context.executeAction(slideIdTypeError, payload, done); return; } + console.log('send to load'); + context.dispatch('LOAD_SLIDE_CONTENT_LOAD', {loadingIndicator: 'true'}); + //context.dispatch('LOAD_SLIDE_CONTENT_LOAD'); + //console.log('get content'); context.service.read('slide.content', payload, {timeout: 20 * 1000}, (err, res) => { if (err) { diff --git a/actions/translateDeckRevision.js b/actions/translateDeckRevision.js new file mode 100644 index 000000000..64f431dbe --- /dev/null +++ b/actions/translateDeckRevision.js @@ -0,0 +1,54 @@ +import UserProfileStore from '../stores/UserProfileStore'; +import ContentStore from '../stores/ContentStore'; +import striptags from 'striptags'; +import TreeUtil from '../components/Deck/TreePanel/util/TreeUtil'; +import {navigateAction} from 'fluxible-router'; +import serviceUnavailable from './error/serviceUnavailable'; +import addActivity from './activityfeed/addActivity'; +const log = require('./log/clog'); +const common = require('../common.js'); + +export default function translateDeckRevision(context, payload, done) { + context.dispatch('START_TRANSLATION', 'success'); + log.info(context); + console.log('action translateDeckRevision: got payload', payload); + //enrich with user id + let user = context.getStore(UserProfileStore).userid; + //if (!user) user = '3'; //NEED TO REMOVE THE LINE + + payload.user = user.toString(); + payload.deckId = context.getStore(ContentStore).selector.id; + payload.jwt = context.getStore(UserProfileStore).jwt; + console.log(payload); + //enrich with root deck id if deck to be revised is not uppermost deck + // let parent = TreeUtil.getParentId(payload.selector); + // payload.root_deck = parent; + context.service.create('deck.translate', payload, null, {timeout: 30 * 1000}, (err, res) => { + if (err) { + //context.dispatch('UPDATE_DECKEDIT_VIEW_STATE', 'error'); + //context.dispatch('SAVE_DECK_REVISION_FAILURE', err); + log.error(context, {filepath: __filename, err: err}); + // context.executeAction(serviceUnavailable, payload, done); + done(); + } else { + //console.log('res:' + res.id); + + // let activity = { + // activity_type: 'edit', + // user_id: String(context.getStore(UserProfileStore).userid), + // content_id: String(newSid), + // content_kind: 'deck' + // }; + // context.executeAction(addActivity, {activity: activity}); + + context.dispatch('END_TRANSLATION', 'success'); + + context.executeAction(navigateAction, { + url: '/deck/' + res.root_deck //ADD HERE NEW DECK ID + }); + + done(); + + } + }); +} diff --git a/actions/user/notifications/loadUserNotifications.js b/actions/user/notifications/loadUserNotifications.js index 59e8336bd..046d2b64d 100644 --- a/actions/user/notifications/loadUserNotifications.js +++ b/actions/user/notifications/loadUserNotifications.js @@ -4,6 +4,10 @@ const log = require('../../log/clog'); export default function loadUserNotifications(context, payload, done) { log.info(context); + + // start loading + context.dispatch('SHOW_NOTIFICATIONS_LOADING', payload); + context.service.read('notifications.list', payload, {timeout: 20 * 1000}, (err, res) => { if (err) { log.error(context, {filepath: __filename}); diff --git a/actions/user/userprofile/uploadPicture.js b/actions/user/userprofile/uploadPicture.js new file mode 100644 index 000000000..735bf0833 --- /dev/null +++ b/actions/user/userprofile/uploadPicture.js @@ -0,0 +1,39 @@ +import MediaStore from '../../../stores/MediaStore'; +import uploadProfilePicture from '../../media/uploadProfilePicture'; +import changeUserData from './changeUserData'; +const log = require('../../log/clog'); + +//This action gets a whole user object and uploads the picture and then saves the user with the picture URL +export default function uploadPicture(context, payload, done) { + log.info(context); + + //prepare file + let file = { + type: 'image/' + payload.filetype, + title: 'profile picture of '+payload.uname, + filesize: payload.filesize, + filename: payload.fileurl, + bytes: payload.picture + }; + + context.executeAction(uploadProfilePicture, file, () => { + // console.log('uploadPicture: uploaded media file', context.getStore(MediaStore).file, context.getStore(MediaStore).file.url, context.getStore(MediaStore).file.thumbnailUrl); + + if (context.getStore(MediaStore).status !== 'success') { + //show error + context.dispatch('EDIT_USER_FAILED', {}); + + return done(); + } + + delete payload.filesize; + delete payload.fileurl; + delete payload.filetype; + payload.picture = context.getStore(MediaStore).file.url; + // console.log('uploadPicture: Now saving user', payload, context.getStore(MediaStore).file.filename); + + context.executeAction(changeUserData, payload, () => { + done(); + }); + }); +} diff --git a/app.js b/app.js index 60f2dbf07..af808ea3f 100644 --- a/app.js +++ b/app.js @@ -44,6 +44,7 @@ import ServiceErrorStore from './stores/ServiceErrorStore'; import PermissionsStore from './stores/PermissionsStore'; import ContentLikeStore from './stores/ContentLikeStore'; import DeckFamilyStore from './stores/DeckFamilyStore'; +import MediaStore from './stores/MediaStore'; import UserReviewStore from './stores/UserReviewStore'; // create new fluxible instance & register all stores @@ -92,7 +93,8 @@ const app = new Fluxible({ PermissionsStore, ContentLikeStore, DeckFamilyStore, - UserReviewStore + UserReviewStore, + MediaStore ] }); diff --git a/assets/css/custom.css b/assets/css/custom.css index 79b3b2f77..e0e02c6f3 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -40,7 +40,7 @@ font-size: 20px; position: absolute; right: 0; - width: 200px; + width: 150px; height: 100vh; } @@ -50,7 +50,7 @@ .top-diff .reveal { position: relative; - width: calc(100% - 200px); + width: calc(100% - 150px); } .top-diff .reveal .slides { @@ -84,6 +84,8 @@ .top-diff .inlineContent { padding: 2%; + display: flex; + flex-direction: column; } @media screen and (max-width: 900px) { @@ -100,30 +102,41 @@ .top-diff .helpers { width: 100vw; position: relative; - height: 180px; - background-color: rgba(255,153,0,0.2); - } - - .top-diff .helpers .inlineContent { - display: flex; - flex-direction: column; - } - - .top-diff .helpers .checkbox { - flex: 1; - } - - .top-diff .helpers .diff-header { - display: none + height: 240px; } } /* HTML DOM Node related */ -.diff-view .added, .modified, .deleted { - padding: 5px; +.diff-view .added, +.diff-view .modified, +.diff-view .deleted { + padding: 5px 5px 5px 5px; margin: 0px -10px; display: inline-block; + position: relative; +} + +.diff-view .added::before, +.diff-view .modified::before, +.diff-view .deleted::before { + color: rgba(0,0,0,0.8); + position: absolute; + font-size: 20px; + right: 5px; + top: -3px; + font-weight: 700; +} + +.diff-view ul, .diff-view ol { + display: flex; + flex-direction: column; +} + +.diff-view ul .added, +.diff-view ul .modified, +.diff-view ul .deleted { + display: inline-table !important; } .diff-view p { @@ -131,29 +144,34 @@ } .diff-view .added { - border: 5px solid #93ca3b; - background-color: rgba(147,202,59,0.2); + border: 2px solid #2196F3; + background-color: rgba(0, 188, 212,0.2); } +.diff-view .added::before { content: "+" } + .diff-view .modified { - border: 5px solid #FFCC00; - background-color: rgba(255,204,0,0.1); + border: 2px solid #FFCC00; + background-color: rgba(255,204,0,0.2); } +.diff-view .modified::before { content: "+/-" } + .diff-view .deleted { - border: 5px solid #ff0030; - background-color: rgba(255,0,48,0.1); + border: 2px solid #ff0030; + background-color: rgba(255,0,48,0.2); } +.diff-view .deleted::before { content: "x" } .diff-view.inverse .added { - border: 5px solid #2ecc71; + border: 2px solid #2ecc71; background-color: rgba(46,204,113,0.2); } .diff-view.inverse .modified { - border: 5px solid #FF9800; - background-color: rgba(255,152,0,0.1); + border: 2px solid #FF9800; + background-color: rgba(255,152,0,0.2); } .diff-view.inverse .deleted { - border: 5px solid #9C27B0; - background-color: rgba(156,39,176,0.1); + border: 2px solid #9C27B0; + background-color: rgba(156,39,176,0.2); } /* Text related */ @@ -171,12 +189,20 @@ line-height: 32px; } -.diff-view.hide-add .added, .diff-view.hide-mod .modified, .diff-view.hide-del .deleted { - border: 5px solid transparent; +.diff-view.hide-add .added, +.diff-view.hide-mod .modified, +.diff-view.hide-del .deleted { + border: 2px solid transparent; background-color: transparent; } +.diff-view.hide-add .added::before, +.diff-view.hide-mod .modified::before, +.diff-view.hide-del .deleted::before { + display: none; +} -.diff-view.hide-text .ins, .diff-view.hide-text .del{ +.diff-view.hide-text .ins, +.diff-view.hide-text .del{ color: #333; background-color: transparent; text-decoration: none; diff --git a/assets/images/templates/1.png b/assets/images/templates/1.png new file mode 100644 index 000000000..85de9548b Binary files /dev/null and b/assets/images/templates/1.png differ diff --git a/assets/images/templates/11.png b/assets/images/templates/11.png new file mode 100644 index 000000000..530ad8f90 Binary files /dev/null and b/assets/images/templates/11.png differ diff --git a/assets/images/templates/11img.png b/assets/images/templates/11img.png new file mode 100644 index 000000000..0b9646a10 Binary files /dev/null and b/assets/images/templates/11img.png differ diff --git a/assets/images/templates/12.png b/assets/images/templates/12.png new file mode 100644 index 000000000..e464d41c0 Binary files /dev/null and b/assets/images/templates/12.png differ diff --git a/assets/images/templates/2.png b/assets/images/templates/2.png new file mode 100644 index 000000000..1f6d06be1 Binary files /dev/null and b/assets/images/templates/2.png differ diff --git a/assets/images/templates/21.png b/assets/images/templates/21.png new file mode 100644 index 000000000..768543a1d Binary files /dev/null and b/assets/images/templates/21.png differ diff --git a/assets/images/templates/22.png b/assets/images/templates/22.png new file mode 100644 index 000000000..c5706908c Binary files /dev/null and b/assets/images/templates/22.png differ diff --git a/assets/images/templates/3.png b/assets/images/templates/3.png new file mode 100644 index 000000000..883e4d8d9 Binary files /dev/null and b/assets/images/templates/3.png differ diff --git a/assets/images/templates/EKDDA.png b/assets/images/templates/EKDDA.png new file mode 100644 index 000000000..58c22af62 Binary files /dev/null and b/assets/images/templates/EKDDA.png differ diff --git a/assets/images/templates/EKDDAeng.png b/assets/images/templates/EKDDAeng.png new file mode 100644 index 000000000..038c7fc17 Binary files /dev/null and b/assets/images/templates/EKDDAeng.png differ diff --git a/assets/images/templates/EKDDAengNofooter.png b/assets/images/templates/EKDDAengNofooter.png new file mode 100644 index 000000000..a61014007 Binary files /dev/null and b/assets/images/templates/EKDDAengNofooter.png differ diff --git a/assets/images/templates/oegtitleslide.png b/assets/images/templates/oegtitleslide.png new file mode 100644 index 000000000..3d136e063 Binary files /dev/null and b/assets/images/templates/oegtitleslide.png differ diff --git a/assets/images/templates/outitleslide.png b/assets/images/templates/outitleslide.png new file mode 100644 index 000000000..c695c3f30 Binary files /dev/null and b/assets/images/templates/outitleslide.png differ diff --git a/assets/images/templates/slidewikislide.png b/assets/images/templates/slidewikislide.png new file mode 100644 index 000000000..2eb74f26a Binary files /dev/null and b/assets/images/templates/slidewikislide.png differ diff --git a/components/AddDeck/AddDeck.js b/components/AddDeck/AddDeck.js index e26fa9e60..06cd61fd7 100644 --- a/components/AddDeck/AddDeck.js +++ b/components/AddDeck/AddDeck.js @@ -167,10 +167,10 @@ class AddDeck extends React.Component { let noOfSlides = String(this.props.ImportStore.noOfSlides); let totalNoOfSlides = String(this.props.ImportStore.totalNoOfSlides); let progressLabel = (totalNoOfSlides === '0' && this.props.ImportStore.uploadProgress < 65) ? 'Uploading file' : - (this.props.ImportStore.uploadProgress === 65) ? 'Converting file' : - (this.props.ImportStore.uploadProgress !== 100) ? 'Importing slide ' + noOfSlides + ' of ' + totalNoOfSlides : - (noOfSlides === totalNoOfSlides) ? 'Slides uploaded!' : - 'Imported ' + noOfSlides + ' of ' + totalNoOfSlides + ' slides';//this should not happen, but user should know in case it does + (this.props.ImportStore.uploadProgress === 65) ? 'Converting file' : + (this.props.ImportStore.uploadProgress !== 100) ? 'Importing slide ' + noOfSlides + ' of ' + totalNoOfSlides : + (noOfSlides === totalNoOfSlides) ? 'Slides uploaded!' : + 'Imported ' + noOfSlides + ' of ' + totalNoOfSlides + ' slides';//this should not happen, but user should know in case it does $('#progresslabel_addDeck_upload').text(parseInt(this.props.ImportStore.uploadProgress) + '% - ' + progressLabel); if (this.props.ImportStore.uploadProgress === 100) { @@ -194,13 +194,13 @@ class AddDeck extends React.Component { confirmButtonClass: 'positive ui button', buttonsStyling: false }) - .then((dismiss) => { - this.handleImportRedirect(); - return true; - }) - .catch(() => { - return true; - }); + .then((dismiss) => { + this.handleImportRedirect(); + return true; + }) + .catch(() => { + return true; + }); } else { swal({ title: 'Error', @@ -210,12 +210,12 @@ class AddDeck extends React.Component { confirmButtonClass: 'negative ui button', buttonsStyling: false }) - .then(() => { - return true; - }) - .catch(() => { - return true; - }); + .then(() => { + return true; + }) + .catch(() => { + return true; + }); } } } @@ -321,7 +321,7 @@ class AddDeck extends React.Component { if (filename.length > 40) filename = filename.substr(0, 40) + ' ...'; - /* let themeOptions = @@ -336,21 +336,21 @@ class AddDeck extends React.Component { ; */ let themeOptions = ; + + + + + + + + + + + + + + + ; // let licenseOptions = - -
- - +
+
+

Add a deck to SlideWiki

+
+
+
+
+
+ + +
+
+ + +
-
-
- - -
-
-
- - {themeOptions} -
+
+ + +
+
+
+ + {themeOptions} +
-
+
-

You can upload existing slides to your new deck. Currently only PowerPoint pptx and OpenOffice odp files are supported.

-
-
-
-
-
- Select file -
- -
-
- {filename ? '"Selected for upload: '+filename+'"' : ''} -
-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
- -
-
- Create deck +

You can upload existing slides to your new deck. Currently only PowerPoint pptx and OpenOffice odp files are supported.

+
+
+
+
+
+ Select file +
+ +
+
+ {filename ? '"Selected for upload: '+filename+'"' : ''} +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+
+ Create deck +
-
- -
+ +
); } diff --git a/components/Application.js b/components/Application.js index 3ea87770d..7b75f2c99 100644 --- a/components/Application.js +++ b/components/Application.js @@ -11,6 +11,7 @@ import ErrorStore from '../stores/ErrorStore'; import Error from './Error/Error'; import { DragDropContext } from 'react-dnd'; import HTML5Backend from 'react-dnd-html5-backend'; +import loadSupportedLanguages from '../actions/loadSupportedLanguages'; class Application extends React.Component { @@ -40,6 +41,10 @@ class Application extends React.Component { } } + componentDidMount() { + context.executeAction(loadSupportedLanguages, {}, () => {return;}); + } + componentDidUpdate(prevProps, prevState) { const newProps = this.props; if (newProps.pageTitle === prevProps.pageTitle) { diff --git a/components/Deck/ActivityFeedPanel/ActivityItem.js b/components/Deck/ActivityFeedPanel/ActivityItem.js index 0f19de8e9..7242c40f9 100644 --- a/components/Deck/ActivityFeedPanel/ActivityItem.js +++ b/components/Deck/ActivityFeedPanel/ActivityItem.js @@ -112,6 +112,18 @@ class ActivityItem extends React.Component { ); break; + case 'move': + IconNode = (); + SummaryNode = ( +
+ + {node.author ? node.author.username : 'unknown'} + {'moved '} {nodeRef} +
+ {DateDiv} +
+ ); + break; case 'comment': IconNode = (); SummaryNode = ( diff --git a/components/Deck/ContentModulesPanel/ContentHistoryPanel/ContentChangeItem.js b/components/Deck/ContentModulesPanel/ContentHistoryPanel/ContentChangeItem.js index a83d898f6..95f48d3da 100644 --- a/components/Deck/ContentModulesPanel/ContentHistoryPanel/ContentChangeItem.js +++ b/components/Deck/ContentModulesPanel/ContentHistoryPanel/ContentChangeItem.js @@ -6,6 +6,8 @@ import {formatDate} from '../../ActivityFeedPanel/util/ActivityFeedUtil'; //TODO import {NavLink} from 'fluxible-router'; +import Iso from 'iso-639-1'; + class ContentChangeItem extends React.Component { handleRevertClick() { @@ -60,6 +62,10 @@ class ContentChangeItem extends React.Component { iconName = 'fork'; description = created a fork of deck {change.value.origin.title}; break; + case 'translate': + iconName = 'translate'; + description = created a translation of deck {change.value.origin.title} into { Iso.getName(change.translatedTo.substring(0, 2)) } ; + break; case 'revise': iconName = 'save'; description = created a new version of {change.oldValue.kind} {change.oldValue.ref.title}; diff --git a/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionAnswersList.js b/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionAnswersList.js index ac9ff7b7d..9d576f7c0 100644 --- a/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionAnswersList.js +++ b/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionAnswersList.js @@ -51,40 +51,43 @@ class ContentQuestionAnswersList extends React.Component { let correctAnswers = this.props.items.filter((item) => item.correct).map((node, index) => { return (
- - {node.answer} - -
-

- {node.explanation} -

-
+ + {node.answer} +
); }); + let explanation = ( +
+

+ {this.props.explanation} +

+
+ ); let answers = ( -
+
-
-
- {list} -
-
+
+
+ {list} +
+
- - {/*showEditButton()*/} -
-
- {this.state.showCorrect ? correctAnswers : null} + + {/*showEditButton()*/} +
+
+ {this.state.showCorrect ? correctAnswers : null} + {this.state.showCorrect ? explanation : null} +
-
-
+
); return ( @@ -92,7 +95,7 @@ class ContentQuestionAnswersList extends React.Component {
{answers}
-
+ ); } } diff --git a/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionsItem.js b/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionsItem.js index f6f478a80..4faf55245 100644 --- a/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionsItem.js +++ b/components/Deck/ContentModulesPanel/ContentQuestionsPanel/ContentQuestionsItem.js @@ -16,7 +16,7 @@ class ContentQuestionsItem extends React.Component { render() { const question = this.props.question; const answers = ( - + ); // const editIcon = ( diff --git a/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js b/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js index 3d7698bb6..b3f95e682 100644 --- a/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js +++ b/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js @@ -57,6 +57,7 @@ class ContentActionsFooter extends React.Component { window.open(this.getPresentationHref()); } } + /* getPrintHref(){ return '/PresentationPrint/' + this.props.ContentStore.selector.id + '/?print-pdf'; @@ -157,19 +158,20 @@ class ContentActionsFooter extends React.Component {
- + - - {/* {this.state.expanded ? : } */} diff --git a/components/Deck/ContentPanel/ContentActions/DownloadButton.js b/components/Deck/ContentPanel/ContentActions/DownloadButton.js deleted file mode 100644 index 0cf0b2f22..000000000 --- a/components/Deck/ContentPanel/ContentActions/DownloadButton.js +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import { Button, Icon,Dropdown} from 'semantic-ui-react'; -import {connectToStores} from 'fluxible-addons-react'; -import ContentStore from '../../../../stores/ContentStore'; -import UserProfileStore from '../../../../stores/UserProfileStore'; -import {Microservices} from '../../../../configs/microservices'; -import addActivity from '../../../../actions/activityfeed/addActivity'; -import incrementDeckViewCounter from '../../../../actions/activityfeed/incrementDeckViewCounter'; - -class DownloadButton extends React.Component{ - constructor(props) { - super(props); - this.dropDown = null; - //this.getExportHref = this.getExportHref.bind(this); - } - getExportHref(type){ - let splittedId; - if (this.props.ContentStore.selector.id !== undefined && this.props.ContentStore.selector.id !== '' && this.props.ContentStore.selector.id !== 0){ - - splittedId = this.props.ContentStore.selector.id.split('-'); //separates deckId and revision - } else { - return ''; //no deckId - } - - switch (type) { - case 'PDF': - return Microservices.pdf.uri + '/exportPDF/' + splittedId[0]; - break; - case 'ePub': - return Microservices.pdf.uri + '/exportEPub/' + splittedId[0]; - break; - case 'SCORMv1.2': - case 'SCORMv2': - case 'SCORMv3': - case 'SCORMv4': - let version = type.split('v'); //separates format from version. In second position we have the version - return Microservices.pdf.uri + '/exportSCORM/' + splittedId[0]+ '?version='+version[1]; - break; - default: - return ''; - - } - - - } - createDownloadActivity() { - //create new activity - let splittedId = this.props.ContentStore.selector.id.split('-'); //separates deckId and revision - let userId = String(this.props.UserProfileStore.userid); - if (userId === '') { - userId = '0';//Unknown - not logged in - } - let activity = { - activity_type: 'download', - user_id: userId, - content_id: splittedId[0], - content_kind: 'deck' - }; - this.context.executeAction(addActivity, {activity: activity}); - context.executeAction(incrementDeckViewCounter, {type: 'download'}); - } - handleDownloadSelection(event,data){ - - if(process.env.BROWSER){ - //event.preventDefault(); - window.open(this.getExportHref(data.value)); - } - this.dropDown.setValue(''); - this.createDownloadActivity(); - - } - - render(){ - - let downloadOptions =[ - {value:'PDF' , text:'PDF'}, - {value:'ePub' , text:'ePub'}, - {value:'SCORMv1.2' , text:'SCORM 1.2'}, - {value:'SCORMv2' , text:'SCORM 2004 (3rd edition)'}, - {value:'SCORMv3' , text:'SCORM 2004 (4th edition)'}, - {value:'SCORMv4' , text:'SCORM 2004 (5th edition)'}, - //{value:'' , text:''}, - - ]; - - let icon_object = ; - /*icon='download large'*/ - return( - {this.dropDown = dropDown;}} - > - - - {/*} - //If we use menu, the component is not key accesible.. - - - - < Dropdown.Divider /> - - - - - - - - - - - */} - - ); - } -} - -DownloadButton.contextTypes = { - executeAction: React.PropTypes.func.isRequired -}; - -DownloadButton = connectToStores(DownloadButton,[ContentStore,UserProfileStore],(context,props) => { - return{ - ContentStore : context.getStore(ContentStore).getState(), - UserProfileStore : context.getStore(UserProfileStore).getState() - }; -}); -export default DownloadButton; diff --git a/components/Deck/ContentPanel/ContentPanel.js b/components/Deck/ContentPanel/ContentPanel.js index 6bd4ed924..0040beb79 100644 --- a/components/Deck/ContentPanel/ContentPanel.js +++ b/components/Deck/ContentPanel/ContentPanel.js @@ -15,7 +15,7 @@ class ContentPanel extends React.Component { switch (this.props.ContentStore.selector.stype) { case 'deck': switch (this.props.ContentStore.mode) { - case 'view': + case 'view': targetComponent = ; break; case 'edit': diff --git a/components/Deck/ContentPanel/DeckModes/DeckEditPanel/DeckPropertiesEditor.js b/components/Deck/ContentPanel/DeckModes/DeckEditPanel/DeckPropertiesEditor.js index ef081d3b8..c8445ea41 100644 --- a/components/Deck/ContentPanel/DeckModes/DeckEditPanel/DeckPropertiesEditor.js +++ b/components/Deck/ContentPanel/DeckModes/DeckEditPanel/DeckPropertiesEditor.js @@ -65,10 +65,10 @@ class DeckPropertiesEditor extends React.Component { allowOutsideClick: true, buttonsStyling: false }) - .then(() => { - return true; - }) - .catch(); + .then(() => { + return true; + }) + .catch(); } else if (newProps.DeckEditStore.viewstate === 'success') { swal({ @@ -81,10 +81,10 @@ class DeckPropertiesEditor extends React.Component { allowOutsideClick: true, buttonsStyling: false }) - .then(() => { - return true; - }) - .catch(); + .then(() => { + return true; + }) + .catch(); } } } @@ -105,7 +105,7 @@ class DeckPropertiesEditor extends React.Component { $(ReactDOM.findDOMNode(this.refs.AddGroups)) .dropdown({ action: (someText, dataValue, source) => { - // console.log('group dropdown select', dataValue); + // console.log('group dropdown select', dataValue); $(ReactDOM.findDOMNode(this.refs.AddGroups)).dropdown('clear'); $(ReactDOM.findDOMNode(this.refs.AddGroups)).dropdown('hide'); @@ -140,7 +140,7 @@ class DeckPropertiesEditor extends React.Component { saveRemoteData: false, action: (name, value, source) => { let data = JSON.parse(decodeURIComponent(value)); - // console.log('user dropdown select', name, value, data); + // console.log('user dropdown select', name, value, data); $(ReactDOM.findDOMNode(this.refs.AddUser)).dropdown('clear'); $(ReactDOM.findDOMNode(this.refs.AddUser)).dropdown('hide'); @@ -149,7 +149,7 @@ class DeckPropertiesEditor extends React.Component { if (users === undefined || users === null) users = []; - // console.log('trying to add', name, 'to', users); + // console.log('trying to add', name, 'to', users); if (users.findIndex((member) => { return member.id === parseInt(data.userid); }) === -1 && parseInt(data.userid) !== this.props.userid) { @@ -278,39 +278,39 @@ class DeckPropertiesEditor extends React.Component { this.handleClickRemoveUser(user, event); }; let optionalElement = (user.organization || user.country) ? ( -
- {user.organization || 'Unknown organization'} ({user.country || 'unknown country'}) -
-
+
+ {user.organization || 'Unknown organization'} ({user.country || 'unknown country'}) +
+
) : ''; let optionalText = (user.joined) ? ('Access granted '+timeSince((new Date(user.joined)))+' ago') : ''; const key = 'user_' + counter + user.username + user.id; // console.log('new authorized user:', user); // console.log('New key for authorized user:', key, user); list_authorized.push( - ( -
-
-
- -
-
-
- + +
{(this.props.UserProfileStore.userid === '') ? captchaField: ''} +
); - // const filters = ( - //
- //
- //

Show notifications for:

- // - //
- //
- //
- // {userSubscriptionList} - //
- //
- //
- // - //
- //
- //
- // {slideSubscriptionList} - //
- //
- //
- // - //
- //
- //
- // {deckSubscriptionList} - //
- //
- //
- //

Show activity types:

- //
- //
- //
- // {activityTypeList} - //
- //
- //
- //
- //
- // ); + + let loadingDiv =
+
Loading
+
; + let emptyDiv =
+

There are currently no notifications.

+
; + + let notificationsDiv=''; + if(this.props.UserNotificationsStore.loading){ + notificationsDiv = loadingDiv; + } else if (notifications.length === 0) { + notificationsDiv = emptyDiv; + } else { + notificationsDiv = ; + } return (
@@ -174,12 +113,7 @@ class UserNotificationsPanel extends React.Component {
- {(!notifications || notifications.length === 0) - ? -
{this.displayEmptyText}
- : - - } + {notificationsDiv}
diff --git a/components/User/UserProfile/ChangePicture.js b/components/User/UserProfile/ChangePicture.js index 724aa2a01..50e4f14b6 100644 --- a/components/User/UserProfile/ChangePicture.js +++ b/components/User/UserProfile/ChangePicture.js @@ -9,6 +9,7 @@ class ChangePicture extends React.Component { constructor(){ super(); this.filePath = ''; + this.filesize = 0; } componentDidMount() { @@ -22,9 +23,26 @@ class ChangePicture extends React.Component { openCropPictureModal(e) { this.filePath = URL.createObjectURL(e.target.files[0]); let toCheck = e.target.files[0].name.toLowerCase().trim(); + this.filesize = e.target.files[0].size; + // console.log('filesize:', this.filesize); + this.filetype = toCheck.substr(toCheck.length - 3); if(toCheck.endsWith('.jpg') || toCheck.endsWith('.jpeg') || toCheck.endsWith('.png')) { - this.forceUpdate(); - $('#ChangePictureModalOpenButton').click(); + if (this.filesize > 10000000) { + swal({ + title: 'Big file', + text: 'The selected file is quite big (> 10MB). This could cause problems like a white profile picture. You should upload a smaller picture if you notice strange things.', + type: 'warning', + confirmButtonClass: 'ui primary button', + buttonsStyling: false + }).then(() => { + this.forceUpdate(); + $('#ChangePictureModalOpenButton').click(); + }); + } + else { + this.forceUpdate(); + $('#ChangePictureModalOpenButton').click(); + } } else swal({ title: 'Wrong file type', @@ -32,7 +50,7 @@ class ChangePicture extends React.Component { type: 'error', confirmButtonClass: 'ui primary button', buttonsStyling: false - }); + }).then(() => {}); //The actual processing of the picture is implemented in the modal } @@ -74,7 +92,7 @@ class ChangePicture extends React.Component {
- + ); } diff --git a/components/User/UserProfile/ChangePictureModal.js b/components/User/UserProfile/ChangePictureModal.js index c43a87446..2e1bc840b 100644 --- a/components/User/UserProfile/ChangePictureModal.js +++ b/components/User/UserProfile/ChangePictureModal.js @@ -1,8 +1,8 @@ import React from 'react'; import FocusTrap from 'focus-trap-react'; import {Cropper} from 'react-image-cropper'; -import changeUserData from '../../../actions/user/userprofile/changeUserData'; -import { Button, Icon, Modal, Container, Segment, Menu,Label,Input,Divider, TextArea} from 'semantic-ui-react'; +import uploadPicture from '../../../actions/user/userprofile/uploadPicture'; +import { Button, Modal, Divider, TextArea} from 'semantic-ui-react'; class ChangePictureModal extends React.Component { @@ -46,9 +46,13 @@ class ChangePictureModal extends React.Component { uploadCroppedPicture(e) { let payload = {}; Object.assign(payload, this.props.user); - payload.picture = this.refs.cropper.crop({ maxWidth: 170 }); + payload.picture = this.refs.cropper.crop({ maxWidth: 170 });//NOTE maxWidth is not used by the cropper + payload.fileurl = this.props.filePath; + payload.filesize = payload.picture.length - 22;// minus 22 for data:image/png;base64, which is the prefix + payload.filetype = 'png'; + // console.log('ChangePictureModal: uploadCroppedPicture:', payload); if(payload.picture.length > 50){ //check if this is a picture or not - if not, the base64 repesentation is about 5 chars - this.context.executeAction(changeUserData, payload); + this.context.executeAction(uploadPicture, payload); this.handleClose(); } else { this.handleClose(); diff --git a/components/User/UserProfile/UserGroupEdit.js b/components/User/UserProfile/UserGroupEdit.js index d54f15563..6b0e84f1a 100644 --- a/components/User/UserProfile/UserGroupEdit.js +++ b/components/User/UserProfile/UserGroupEdit.js @@ -15,7 +15,7 @@ class UserGroupEdit extends React.Component { } componentDidUpdate() { - console.log('UserGroupEdit componentDidUpdate:', this.props.saveUsergroupError); + console.log('UserGroupEdit componentDidUpdate:', this.props.saveUsergroupError, this.props.currentUsergroup); if (this.props.saveUsergroupError) { swal({ title: 'Error', @@ -33,6 +33,8 @@ class UserGroupEdit extends React.Component { .catch(); return; } + this.refs.GroupName.value = this.props.currentUsergroup.name; + this.refs.GroupDescription.value = this.props.currentUsergroup.description; } componentDidMount() { @@ -141,10 +143,6 @@ class UserGroupEdit extends React.Component { const signUpLabelStyle = {width: '150px'}; let userlist = []; - let prefs = { - name: this.props.currentUsergroup.name, - desc: this.props.currentUsergroup.description - }; //change header and data depending on group should be created or edited let header = 'Create Group'; if (this.props.currentUsergroup._id !== undefined) { @@ -222,12 +220,12 @@ class UserGroupEdit extends React.Component { - +
- +
diff --git a/components/User/UserProfile/UserGroups.js b/components/User/UserProfile/UserGroups.js index 7dc0436e2..9b67853f1 100644 --- a/components/User/UserProfile/UserGroups.js +++ b/components/User/UserProfile/UserGroups.js @@ -38,16 +38,16 @@ class UserGroups extends React.Component { handleClickOnEditGroup(e) { e.preventDefault(); - console.log('handleClickOnEditGroup:', e.target.attributes.name.nodeValue); + // console.log('handleClickOnEditGroup:', e.target.attributes.name.value); - const action = e.target.attributes.name.nodeValue; //eg. changeGroup_2 + const action = e.target.attributes.name.value; //eg. changeGroup_2 const groupid = action.split('_')[1]; let group = this.props.groups.find((group) => { return group._id.toString() === groupid; }); - console.log('handleClickOnEditGroup: use group', group); + // console.log('handleClickOnEditGroup: use group', group); this.context.executeAction(updateUsergroup, {group: group, offline: false}); @@ -58,9 +58,9 @@ class UserGroups extends React.Component { handleClickOnRemoveGroup(e) { e.preventDefault(); - console.log('handleClickOnRemoveGroup:', e.target.attributes.name.nodeValue); + console.log('handleClickOnRemoveGroup:', e.target.attributes.name.value); - const action = e.target.attributes.name.nodeValue; //eg. changeGroup_2 + const action = e.target.attributes.name.value; //eg. changeGroup_2 const groupid = action.split('_')[1]; this.context.executeAction(deleteUsergroup, {groupid: groupid}); @@ -68,9 +68,9 @@ class UserGroups extends React.Component { handleClickOnLeaveGroup(e) { e.preventDefault(); - console.log('handleClickOnLeaveGroup:', e.target.attributes.name.nodeValue); + console.log('handleClickOnLeaveGroup:', e.target.attributes.name.value); - const action = e.target.attributes.name.nodeValue; //eg. changeGroup_2 + const action = e.target.attributes.name.value; //eg. changeGroup_2 const groupid = action.split('_')[1]; this.context.executeAction(leaveUsergroup, {groupid: groupid}); diff --git a/components/User/UserProfile/UserProfile.js b/components/User/UserProfile/UserProfile.js index 492db5a10..a4db83cc7 100644 --- a/components/User/UserProfile/UserProfile.js +++ b/components/User/UserProfile/UserProfile.js @@ -31,7 +31,8 @@ class UserProfile extends React.Component { }) .then(() => { },() => {//dismiss function - if(this.props.IntlStore.currentLocale !== getIntlLanguage()) //user to reload page beacuse of cookie change + if(this.props.IntlStore.currentLocale !== getIntlLanguage() || + (this.props.UserProfileStore.category === categories.categories[0] && this.props.UserProfileStore.categoryItem === categories.settings[0]) ) //user to reload page beacuse of cookie change or picture change window.location.reload(); }).catch(swal.noop); if (this.props.UserProfileStore.dimmer.userdeleted === true) diff --git a/components/common/LanguageDropdown.js b/components/common/LanguageDropdown.js index 4d540eea0..71aa37549 100644 --- a/components/common/LanguageDropdown.js +++ b/components/common/LanguageDropdown.js @@ -42,12 +42,16 @@ class LanguageDropdown extends React.Component { }); let languageOptions =
+
English
German
+
+ Dutch +
Greek
diff --git a/components/common/UploadMediaModal.js b/components/common/UploadMediaModal.js new file mode 100644 index 000000000..dcfde42c8 --- /dev/null +++ b/components/common/UploadMediaModal.js @@ -0,0 +1,265 @@ +import React from 'react'; +import Dropzone from 'react-dropzone'; +import FocusTrap from 'focus-trap-react'; +import {Button, Icon, Image, Input, Modal, Divider, TextArea, Dropdown, Popup} from 'semantic-ui-react'; +import uploadMediaFiles from '../../actions/media/uploadMediaFiles'; +import { connectToStores, provideContext } from 'fluxible-addons-react'; +import {isEmpty} from '../../common'; + +class UploadMediaModal extends React.Component { + + constructor(props) { + super(props); + + this.state = { + openModal: false, + activeTrap: false, + active: true, + files: [], + license: false, + licenseValue: 'CC0', + copyrightHolder: '', + alt: '', + title: '', + isLoading: false + }; + + this.handleOpen = this.handleOpen.bind(this); + this.handleClose = this.handleClose.bind(this); + this.unmountTrap = this.unmountTrap.bind(this); + this.showLicense = this.showLicense.bind(this); + this.submitPressed = this.submitPressed.bind(this); + } + + componentDidUpdate(prevProps, prevState) { + if(prevState.files !== this.state.files && !isEmpty(this.state.files)){ + //TODO Bad approach to set focus, but setting it without timeout does not work + setTimeout(() => { + this.refs.UploadMediaModalSaveButton.focus(); + }, 100); + } + } + + handleChange(e) { + this.setState({ [e.target.name]: e.target.value }); + } + + handleOpen(){ + $('#app').attr('aria-hidden','true'); + this.setState({ + modalOpen:true, + activeTrap:true, + isLoading: false + }); + } + + handleClose(){ + $('#app').attr('aria-hidden','false'); + this.setState({ + modalOpen:false, + activeTrap: false, + files: [], + license: false, + licenseValue: 'CC0', + copyrightHolder: '', + alt: '', + title: '', + isLoading: false + }); + } + + unmountTrap(){ + if(this.state.activeTrap){ + this.setState({ activeTrap: false }); + $('#app').attr('aria-hidden','false'); + } + } + + showLicense() { + this.setState({ + license: true + }); + } + + onDrop(files) { + this.setState({ + files + }); + } + + changeLicense(event, data) { + this.setState({ + licenseValue: data.value + }); + } + + submitPressed(e) { + e.preventDefault(); + let that = this; + if(this.state.copyrightHolder === undefined || this.state.copyrightHolder === ''){this.state.copyrightHolder = this.props.userFullName;} + console.log('copyrighthodler: ' + this.state.copyrightHolder); + let payload = { + type: this.state.files[0].type, + license: this.state.licenseValue, + copyrightHolder: this.state.copyrightHolder, + title: this.state.title || this.state.files[0].name, + text: this.state.alt, + filesize: this.state.files[0].size, + filename: this.state.files[0].name, + bytes: null + }; + console.log(this.state, payload); + + let reader = new FileReader(); + + reader.onloadend = function (evt) { + console.log('read total length from file: ', reader.result.length, evt.target.readyState); + + if (evt.target.readyState === FileReader.DONE) { + payload.bytes = reader.result; + that.context.executeAction(uploadMediaFiles, payload); + + that.setState({ + isLoading: true + }); + } + }; + + reader.onerror = (err) => { + swal({ + title: 'Error', + text: 'Reading the selected file failed. Check you privileges and try again.', + type: 'error', + confirmButtonText: 'Close', + confirmButtonClass: 'negative ui button', + allowEscapeKey: false, + allowOutsideClick: false, + buttonsStyling: false + }) + .then(() => { + return true; + }); + }; + + reader.readAsDataURL(this.state.files[0]); + + return false; + } + + render() { + this.context.getUser().username; + let dropzone = ''; + if(this.state.files.length < 1){ + dropzone =
+ + +

Drop a file directly from your filebrowser here to upload it.

Alternatively, click or anywhere around this text to select a file to upload.

+
+
; + } else { //TODO Implement a switch-case statement for other media files. Currently only works for images. + dropzone =
+ + + +
+

Not the right image? Click on the image to upload another one.

; + } + //let heading = 'Upload a media file'; + let heading = 'Add image - upload image file from your computer'; + let content =
+