From 8ea664a4fd40d9c9f16a07c23435acc0253a1384 Mon Sep 17 00:00:00 2001 From: kadevgraaf Date: Mon, 4 Dec 2017 14:38:12 +0100 Subject: [PATCH] merge with master --- .gitignore | 1 + .gitmodules | 5 +- .travis.yml | 1 + actions/activityfeed/likeActivity.js | 5 + actions/activityfeed/loadActivities.js | 12 +- actions/decktree/moveTreeNode.js | 10 + actions/loadSupportedLanguages.js | 17 + actions/loadTranslations.js | 34 +- actions/media/uploadMediaFile.js | 1 - actions/media/uploadMediaFiles.js | 44 + actions/translateDeckRevision.js | 54 + .../notifications/loadUserNotifications.js | 4 + components/AddDeck/AddDeck.js | 204 ++-- components/Application.js | 5 + .../Deck/ActivityFeedPanel/ActivityItem.js | 12 + .../ContentHistoryPanel/ContentChangeItem.js | 6 + .../ContentActions/ContentActionsFooter.js | 8 +- components/Deck/ContentPanel/ContentPanel.js | 2 +- .../DeckEditPanel/DeckPropertiesEditor.js | 296 ++--- .../DeckModes/DeckViewPanel/DeckViewPanel.js | 85 +- .../SlideEditPanel/SlideContentEditor.js | 127 +- components/Deck/Deck.js | 51 +- components/Deck/InfoPanel/InfoPanel.js | 5 - .../Deck/InfoPanel/InfoPanelInfoView.js | 45 +- .../Deck/InfoPanel/PresentationsPanel.js | 114 ++ components/Deck/NavigationPanel/Breadcrumb.js | 2 +- .../Deck/SlideEditPanel/SlideEditPanel.js | 70 ++ .../Deck/TranslationPanel/TranslationItem.js | 13 - .../Deck/TranslationPanel/TranslationList.js | 19 - .../Deck/TranslationPanel/TranslationPanel.js | 138 ++- components/Deck/TreePanel/TranslationModal.js | 161 +++ components/Deck/TreePanel/TreePanel.js | 51 +- components/DefaultHTMLLayout.js | 5 +- components/Footer/Footer.js | 5 +- components/Home/Featured.js | 18 + components/Home/Features.js | 4 +- components/Home/Home.js | 24 +- components/Home/Home_OLD.js | 2 +- components/Home/Recent.js | 26 +- components/Home/Terms.js | 109 ++ components/Home/Welcome.js | 2 +- components/PresentationRoomsHTMLLayout.js | 55 + components/PresentorHTMLLayout.js | 82 ++ components/Search/SearchPanel.js | 1 + .../UserNotificationsItem.js | 14 + .../UserNotificationsPanel.js | 102 +- components/User/UserProfile/UserGroupEdit.js | 12 +- components/User/UserProfile/UserGroups.js | 14 +- components/common/LanguageDropdown.js | 4 + components/common/UploadMediaModal.js | 265 +++++ components/common/UserPicture.js | 11 +- components/webrtc/Chat.js | 155 +++ components/webrtc/SpeechRecognition.js | 232 ++++ components/webrtc/presentationBroadcast.js | 1056 +++++++++++++++++ configs/microservices.sample.js | 10 + configs/routes.js | 64 +- configs/version.js | 5 - docker-compose.yml | 3 + microservices.js.template | 14 +- package.json | 38 +- plugins/googleAnalytics/ga.js | 2 +- server.js | 1 + server/handleServerRendering.js | 11 +- services/deck.js | 16 + services/like.js | 7 +- services/media.js | 20 +- services/presentation.js | 12 +- services/translation.js | 51 +- stores/ActivityFeedStore.js | 11 +- stores/DeckPageStore.js | 30 +- stores/DeckTreeStore.js | 28 +- stores/MediaStore.js | 3 +- stores/TranslationStore.js | 35 +- stores/UserNotificationsStore.js | 14 +- stores/UserProfileStore.js | 2 +- test/components/UserPicture.test.js | 43 + test/mocha.opts | 1 - test/setup.js | 14 +- test/unit/ContentModulesStore.js | 3 +- test/unit/index.html | 10 - 80 files changed, 3599 insertions(+), 649 deletions(-) create mode 100644 actions/loadSupportedLanguages.js create mode 100644 actions/media/uploadMediaFiles.js create mode 100644 actions/translateDeckRevision.js create mode 100644 components/Deck/InfoPanel/PresentationsPanel.js create mode 100644 components/Deck/SlideEditPanel/SlideEditPanel.js delete mode 100644 components/Deck/TranslationPanel/TranslationItem.js delete mode 100644 components/Deck/TranslationPanel/TranslationList.js create mode 100644 components/Deck/TreePanel/TranslationModal.js create mode 100644 components/Home/Featured.js create mode 100644 components/Home/Terms.js create mode 100644 components/PresentationRoomsHTMLLayout.js create mode 100644 components/PresentorHTMLLayout.js create mode 100644 components/common/UploadMediaModal.js create mode 100644 components/webrtc/Chat.js create mode 100644 components/webrtc/SpeechRecognition.js create mode 100644 components/webrtc/presentationBroadcast.js delete mode 100644 configs/version.js create mode 100644 test/components/UserPicture.test.js delete mode 100644 test/mocha.opts delete mode 100644 test/unit/index.html 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 6f609cc0a..2e3af2090 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,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/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 index 0bca4a318..78c3c0b36 100644 --- a/actions/media/uploadMediaFile.js +++ b/actions/media/uploadMediaFile.js @@ -8,7 +8,6 @@ export default function uploadMediaFile(context, payload, done) { payload.userid = context.getStore(UserProfileStore).userid; payload.jwt = context.getStore(UserProfileStore).jwt; - console.log('uploadMediaFile', payload); 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) => { 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/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/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/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/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( - ( -
-
-
- -
-
-
- +
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/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 =
+