-
+
+
+
+
-
- {file.name}
-
+
{file.name}
+
+
+
+
-
- );
- })}
+ { this.getFileDeleteModalUI(file) }
+
+ );
+ }
+
+ getFilesUI() {
+ return (
+
+ {this.props.files.map((file) => {
+ return this.getFileUI(file);
+ })}
);
}
diff --git a/src/modules/file-explorer/components/upload.jsx b/src/modules/file-explorer/components/upload.jsx
index da28102ee5..5b6d16b68d 100644
--- a/src/modules/file-explorer/components/upload.jsx
+++ b/src/modules/file-explorer/components/upload.jsx
@@ -1,4 +1,5 @@
-import { Link } from 'react-router';
+import React from 'react';
+import Dropzone from 'react-dropzone';
require('../styles/upload.scss');
@@ -10,19 +11,16 @@ class Memory extends React.Component {
this.state = {};
}
- /* Mock data */
- getData() {
- return [{}];
- }
-
render() {
return (
-
- Dateien zum Hochladen ablegen.
-
+
+ Dateien zum Hochladen ablegen.
+
diff --git a/src/modules/file-explorer/containers/file-explorer.js b/src/modules/file-explorer/containers/file-explorer.js
index f2c8920f5c..542fd891ca 100644
--- a/src/modules/file-explorer/containers/file-explorer.js
+++ b/src/modules/file-explorer/containers/file-explorer.js
@@ -1,16 +1,29 @@
import {render} from 'react-dom';
import {compose} from 'react-komposer';
+import { FileService } from '../../core/helpers';
+import { Permissions, Server, Notification, RandomIdGenerator } from '../../core/helpers/';
import component from '../components/file-explorer';
import actions from '../actions/file-explorer';
const composer = (props, onData) => {
+ const currentUser = Server.get('user');
+ FileService.getFileList(`users/${currentUser._id}`)
+ .then(res => {
+ let componentData = {actions, files: []};
- let componentData = {
- actions
- };
+ // if the storage provider does not return any files, there's no empty array but an ugly error message
+ if( Object.prototype.toString.call( res ) === '[object Array]' ) {
+ componentData.files = res;
+ componentData.files.forEach(f => f.id = RandomIdGenerator.generateRandomId());
+ } else {
+ Notification.showError("Deine Schule hat bislang noch keine Dateiverwaltung ausgewählt");
+ }
- onData(null, componentData);
+ onData(null, componentData);
+ }).catch(err => {
+ Notification.showError(err);
+ });
};
export default compose(composer)(component);
diff --git a/src/modules/file-explorer/styles/files.scss b/src/modules/file-explorer/styles/files.scss
index 157dea9adb..d9574022d3 100644
--- a/src/modules/file-explorer/styles/files.scss
+++ b/src/modules/file-explorer/styles/files.scss
@@ -1,37 +1,41 @@
@import '../../core/styles/colors.scss';
.files {
- width:100%;
- float:left;
- margin-bottom:40px;
+ width: 100%;
+ float: left;
+ margin-bottom: 40px;
h5 {
color: $colorHPIRed;
- padding:10px 0px 20px;
+ padding: 10px 0px 20px;
}
- .card.file {
- box-shadow: 0px 1px 2px rgba(0,0,0,0.1);
- height:65px;
- line-height:63px;
+ .modal-body {
+ button {
+ margin-left: 5px;
+ }
+ }
+ .card.file {
&:hover {
box-shadow: 0px 1px 4px rgba(0,0,0,0.2);
- background:#f9f9f9;
- cursor: pointer;
+ background: #f9f9f9;
}
.file-preview {
- width:90%;
- height:65px;
- float:left;
+ width: 90%;
+ height: 65px;
+ float: left;
background: no-repeat center center;
- background-size:cover;
+ background-size: cover;
}
.fa {
- margin-right:15px;
+ margin-right: 15px;
color:#ccc;
+ &:hover {
+ cursor: pointer;
+ }
}
}
}
diff --git a/src/modules/file-explorer/styles/upload.scss b/src/modules/file-explorer/styles/upload.scss
index cc51e69715..e5cf483467 100644
--- a/src/modules/file-explorer/styles/upload.scss
+++ b/src/modules/file-explorer/styles/upload.scss
@@ -4,6 +4,7 @@
width:100%;
float:left;
padding:40px 0px;
+ cursor: pointer;
.drop-zone {
width:100%;
diff --git a/src/modules/index.js b/src/modules/index.js
index d5d0be86fb..31e49c8ff2 100644
--- a/src/modules/index.js
+++ b/src/modules/index.js
@@ -3,7 +3,9 @@ import Core from './core';
import Static from './static';
import Login from './login';
-import Backend from './backend';
+import Signup from './signup';
+
+import Base from './base';
import Dashboard from './dashboard';
import Content from './content';
import Settings from './settings';
@@ -11,13 +13,13 @@ import Lessons from './lessons';
import Timetable from './timetable';
import FileExplorer from './file-explorer';
import ActiveStudents from './active-students';
-import ErrorPage from './error-page';
import Tools from './tools';
import Administration from './administration';
+import Courses from './courses';
export default {
Core,
- Backend,
+ Base,
Dashboard,
Settings,
Login,
@@ -27,7 +29,8 @@ export default {
FileExplorer,
ActiveStudents,
Administration,
- ErrorPage,
Tools,
- Static
+ Static,
+ Signup,
+ Courses
};
diff --git a/src/modules/lessons/components/lesson.jsx b/src/modules/lessons/components/lesson.jsx
index c58dc67a92..8a1d5c170a 100755
--- a/src/modules/lessons/components/lesson.jsx
+++ b/src/modules/lessons/components/lesson.jsx
@@ -1,7 +1,7 @@
-import LayoutBackend from '../../backend/containers/layout';
-import SectionTitle from '../../backend/components/title';
+import LayoutBase from '../../base/containers/layout';
+import SectionTitle from '../../base/components/title';
import SectionHomework from './homework';
import SectionMaterial from './material';
@@ -26,11 +26,11 @@ class Lesson extends React.Component {
render() {
return (
-
+
-
+
);
}
diff --git a/src/modules/lessons/containers/lesson.js b/src/modules/lessons/containers/lesson.js
index b010789521..75213f75d6 100644
--- a/src/modules/lessons/containers/lesson.js
+++ b/src/modules/lessons/containers/lesson.js
@@ -2,7 +2,7 @@ import {browserHistory} from 'react-router';
import {render} from 'react-dom';
import {compose} from 'react-komposer';
-import { Permissions, Server } from '../../core/helpers/';
+import {Permissions, Server} from '../../core/helpers/';
import permissions from '../permissions';
import component from '../components/lesson';
@@ -10,15 +10,17 @@ import actions from '../actions/lesson';
const composer = (props, onData) => {
const currentUser = Server.get('user');
- if(Permissions.userHasPermission(currentUser, permissions.VIEW)) {
- let componentData = {
- actions
- };
-
- onData(null, componentData);
- } else {
+ if (!Permissions.userHasPermission(currentUser, permissions.VIEW)) {
onData(new Error('You don\'t have the permission to see this page.'));
+ return;
}
+
+ let componentData = {
+ actions
+ };
+
+ onData(null, componentData);
+
};
export default compose(composer)(component);
diff --git a/src/modules/login/actions/login.js b/src/modules/login/actions/login.js
index e20525687e..04a10bf5b1 100644
--- a/src/modules/login/actions/login.js
+++ b/src/modules/login/actions/login.js
@@ -2,34 +2,85 @@ import React from 'react';
import {browserHistory} from 'react-router';
import { Server, Notification } from '../../core/helpers';
+import signupActions from '../../signup/actions/signup';
+
const authService = Server.service('/auth');
+const schoolService = Server.service('/schools');
+const systemService = Server.service('/systems');
+const accountService = Server.service('/accounts');
+
+const authenticate = ({username, password}) => {
+ return Server.authenticateUser({
+ strategy: 'local',
+ username,
+ password,
+ storage: window.localStorage
+ });
+};
+
+function capitalizeFirstLetter(string) {
+ return string.substr(0, 1).toUpperCase() + string.substr(1);
+}
+
+function mapFromArray(array, indexedByProperty) {
+ let map = {};
+ array.forEach(element => {
+ const key = element[indexedByProperty];
+ map[key] = element;
+ });
+ return map;
+}
export default {
- login: ({email, password, school, system}) => {
-
- // generate JWT
- authService.create({username: email, password: password, systemId: system})
- .then(user => {
-
- if (user.token) {
-
- // Login with JWT
- Server.authenticate({
- type: 'token',
- 'token': user.token,
- path: '/auth'
- }).then(function(result) {
- console.info('Authenticated!', Server.get('token'));
- browserHistory.push('/dashboard/');
- }).catch(function(error) {
- console.error('Error authenticating!', error);
- Notification.showError('Error authenticating!');
+ login: ({schoolId, systemId, password, username}) => {
+ const query = {username};
+
+ if(systemId) {
+ query.systemId = systemId;
+ } else {
+ query.systemId = {"$exists": false};
+ }
+
+ // check if account already exists
+ return accountService.find({query}).then((result) => {
+
+ // account exists => login with _id from account
+ if(result.length) {
+ // we can't just use account to login as it has hashed password
+ return authenticate({username, password});
+ } else {
+ // account exists not => create SSO Account
+ if(!systemId) return new Error('Account not found');
+
+ return signupActions.signupAccount({username, schoolId, systemId, password})
+ .then(() => {
+ return authenticate({username, password});
+ }).catch(() => {
+ // account credentials not valid
+ return new Error('Could not create new SSO account for this credentials.');
});
- }
- })
- .catch(error => {
- Notification.showError(error.message);
- return false;
+ }
+ });
+ },
+
+ loadSchools: () => {
+ // load schools
+ // returns an object that maps from ids to schools
+ return schoolService.find()
+ .then(result => {
+ let schools = result.data || [];
+ let schoolsMap = mapFromArray(schools, '_id');
+ return Promise.resolve(schoolsMap);
+ });
+ },
+
+ loadSystems: (school) => {
+ return Promise.all(school.systems.map(id => systemService.get(id)))
+ .then(systems => {
+ systems.forEach(s => {
+ s.type = capitalizeFirstLetter(s.type);
+ });
+ return systems;
});
}
};
diff --git a/src/modules/login/components/login_form.jsx b/src/modules/login/components/login_form.jsx
index 1cb841a556..d312dc5d05 100755
--- a/src/modules/login/components/login_form.jsx
+++ b/src/modules/login/components/login_form.jsx
@@ -1,91 +1,45 @@
-class LoginForm extends React.Component {
-
- constructor(props) {
- super(props);
-
- this.state = {
- email: '',
- password: ''
- }
- }
-
- handleFieldChange(key, event) {
- let newState = this.state || {};
- newState[key] = event.target.value;
-
- this.setState(newState);
- }
-
- handleLogin(e) {
- this.props.actions.login.bind(this)({
- email: this.state.email,
- password: this.state.password,
- school: this.state.school || undefined,
- system: this.state.system || undefined,
- });
- }
+import SystemSelector from './systemSelector';
- loadSystems(event) {
- this.setState({school: event.target.value});
+import {
+ Form,
+ Input
+} from '../../core/helpers/form';
- const schoolId = event.target.value;
- const systems = this.props.schools[schoolId].systems;
- this.setState({systems: systems});
- if(systems.length === 1) {
- this.setState({system: systems[0]}); // automatically select the only system
- }
- }
-
- getSchoolsUI() {
- if(!this.props.schools) return '';
-
- return (
-
- );
- }
+class LoginForm extends React.Component {
- getSystemsUI() {
- if(!this.state.systems) return '';
- const systems = this.state.systems || [];
- if (systems.length == 1 && this.state.system) {
- const system = this.state.system;
- return (
-
- );
- }
- if (systems.length < 2) return '';
- return (
-
- );
+ constructor(props) {
+ super(props);
}
render() {
return (
-
-
- {this.getSchoolsUI()}
- {this.getSystemsUI()}
-
+
);
}
diff --git a/src/modules/login/components/systemSelector.jsx b/src/modules/login/components/systemSelector.jsx
new file mode 100644
index 0000000000..42dc07e869
--- /dev/null
+++ b/src/modules/login/components/systemSelector.jsx
@@ -0,0 +1,116 @@
+import {
+ ReactSelect
+} from '../../core/helpers/form';
+
+class SystemSelector extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ showSelectors: false,
+ schools: [],
+ systems: []
+ };
+ }
+
+ showSchoolSystemSelectors() {
+ this.setState({showSelectors: true});
+ this.loadSchools();
+ }
+
+ loadSchools() {
+ this.props.actions.loadSchools()
+ .then(schools => this.setState({schools}))
+ .catch(e => console.error(e));
+ }
+
+ loadSystems(schoolId) {
+ if(!schoolId) {
+ this.setState({systems: []});
+ return;
+ }
+
+ const selectedSchool = this.state.schools[schoolId];
+ this.props.actions.loadSystems(selectedSchool)
+ .then(systems => this.setState({systems}))
+ .catch(e => console.error(e));
+ }
+
+ getSchoolsUI() {
+ let schools = Object.values(this.state.schools) || [];
+ if(!schools.length) return;
+
+ schools = schools.map(school => ({
+ label: school.name,
+ value: school._id
+ }));
+
+ return (
+
+ );
+ }
+
+ getSystemsUI() {
+ let systems = Object.values(this.state.systems) || [];
+ if(!systems.length) return;
+
+ systems = systems.map(system => ({
+ label: system.type,
+ value: system._id
+ }));
+
+ let selectedSystem;
+ if (systems.length == 1) {
+ selectedSystem = systems[0].value;
+ }
+
+ return (
+
+ );
+ }
+
+ render() {
+ if(this.state.showSelectors) {
+ return (
+
+ {this.getSchoolsUI()}
+ {this.getSystemsUI()}
+
+ );
+ } else {
+ return (
+
+ )
+ }
+ }
+
+}
+
+export default SystemSelector;
diff --git a/src/modules/login/containers/login.js b/src/modules/login/containers/login.js
index 2e5252b7b5..aca4621521 100644
--- a/src/modules/login/containers/login.js
+++ b/src/modules/login/containers/login.js
@@ -5,7 +5,7 @@ import {compose} from 'react-komposer';
import component from '../components/login';
import actions from '../actions/login';
-import { Server } from '../../core/helpers';
+import { Server, Notification } from '../../core/helpers';
const schoolService = Server.service('/schools');
const systemService = Server.service('/systems');
@@ -14,6 +14,7 @@ function composer(props, onData) {
if(Server.get('user')) {
browserHistory.push('/dashboard/');
console.info('Already loggedin, redirect to dashboard');
+ return;
} else {
// load schools
schoolService.find()
@@ -45,7 +46,22 @@ function composer(props, onData) {
let componentData = {
actions,
- schools: schoolsObject
+ reference: props.location.query.ref,
+ schools: schoolsObject,
+ onLogin: (data) => {
+ return actions.login(data).then((result = {}) => {
+ // if userId this means account has connected user
+ if(result.userId) {
+ return browserHistory.push('/dashboard/');
+ } else if(result.accountId) {
+ return browserHistory.push(`/signup/a/${result.accountId}/${data.schoolId}/`);
+ } else {
+ throw new Error('Wrong credentials.');
+ }
+ }).catch((e) => {
+ Notification.showError(e.message);
+ });
+ }
};
onData(null, componentData);
diff --git a/src/modules/login/styles/login.scss b/src/modules/login/styles/login.scss
index 0bb82a4a85..e42d41116f 100644
--- a/src/modules/login/styles/login.scss
+++ b/src/modules/login/styles/login.scss
@@ -15,18 +15,13 @@
margin:100px 0px;
}
- .btn {
- float:right;
- }
-
.form-group {
z-index:10;
position: relative;
}
- select, input {
+ .select, .Select, select, input {
margin-bottom:10px;
- border: 1px solid rgba(0, 0, 0, 0.15);
}
.bg-graphic {
diff --git a/src/modules/settings/components/settings.jsx b/src/modules/settings/components/settings.jsx
index b7ab99341b..825ec38f9f 100755
--- a/src/modules/settings/components/settings.jsx
+++ b/src/modules/settings/components/settings.jsx
@@ -1,5 +1,5 @@
-import LayoutBackend from '../../backend/containers/layout';
-import SectionTitle from '../../backend/components/title';
+import LayoutBase from '../../base/containers/layout';
+import SectionTitle from '../../base/components/title';
import SectionNotifications from './notifications';
import SectionPrivacy from './privacy';
@@ -14,11 +14,11 @@ class Settings extends React.Component {
render() {
return (
-
+
-
+
);
}
diff --git a/src/modules/settings/styles/notifications.scss b/src/modules/settings/styles/notifications.scss
index 806991fe37..9bb703723b 100644
--- a/src/modules/settings/styles/notifications.scss
+++ b/src/modules/settings/styles/notifications.scss
@@ -3,7 +3,6 @@
.section-notifications {
width:100%;
float:left;
- border-bottom:2px solid #696969;
padding: 15px 0px 50px;
line-height: 1.75rem;
diff --git a/src/modules/signup/actions/signup.js b/src/modules/signup/actions/signup.js
new file mode 100644
index 0000000000..4b87d075c0
--- /dev/null
+++ b/src/modules/signup/actions/signup.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import {browserHistory} from 'react-router';
+import { Server, Notification } from '../../core/helpers';
+
+const accountService = Server.service('/accounts');
+const userService = Server.service('/users');
+const schoolService = Server.service('/schools');
+
+const afterSignupUserRegular = (user, data) => {
+ data.userId = user._id;
+ data.username = user.email;
+
+ return accountService.create(data).then((account) => {
+ return Server.authenticateUser({
+ strategy: 'local',
+ username: data.username,
+ password: data.password,
+ storage: window.localStorage
+ }).then(_ => {
+ return Promise.resolve(account);
+ });
+ });
+};
+
+const afterSignupUserSSO = (user, {accountId}) => {
+ return accountService.patch(accountId, {
+ userId: user._id
+ }).then(account => {
+ return Server.authenticateUser().then(_ => {
+ return Promise.resolve(account);
+ });
+ });
+};
+
+export default {
+ signupAccount(data) {
+ return accountService.create(data);
+ },
+
+ signupUser(data) {
+ let userPromise;
+
+ if(data.userId) {
+ userPromise = userService.update(data.userId, data);
+ } else {
+ userPromise = userService.create(data);
+ }
+
+ return userPromise.then((user) => {
+ if(data.accountId) {
+ return afterSignupUserSSO(user, {accountId: data.accountId});
+ } else {
+ return afterSignupUserRegular(user, data);
+ }
+ });
+ },
+
+ updateSchool(data) {
+ if (data._id) return schoolService.patch(data._id, data);
+ return schoolService.create(data);
+ },
+
+ finishSetup(userId) {
+ return userService.patch(userId, {
+ system: {
+ finishedSignup: true
+ }
+ });
+ }
+};
+
+
diff --git a/src/modules/signup/components/setup-form-classes.jsx b/src/modules/signup/components/setup-form-classes.jsx
new file mode 100644
index 0000000000..1ec5cd452f
--- /dev/null
+++ b/src/modules/signup/components/setup-form-classes.jsx
@@ -0,0 +1,51 @@
+import {
+ Checkbox,
+ CheckboxGroup,
+ Icon,
+ Input,
+ RadioGroup,
+ Row,
+ Select,
+ File,
+ Textarea,
+ ReactSelect,
+ Form
+} from '../../core/helpers/form';
+import {Link} from 'react-router';
+
+import Table from '../../administration/components/table';
+import AdminSectionClasses from '../../administration/containers/classes';
+
+class SetupFormClasses extends AdminSectionClasses {
+
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Administration:
Klassen
+
+
Sie können nun Klassen anlegen und diesen Lehrkräfte zuweisen.
+
+
Tipp: Lehrer können auch selber Klassen anlegen.
+
+
+
+
+
+
Im nächsten Schritt können Sie Kurse anlegen:
+
+
Fortsetzen
+
+ );
+
+ }
+
+}
+
+export default SetupFormClasses;
+
+
+
diff --git a/src/modules/signup/components/setup-form-courses.jsx b/src/modules/signup/components/setup-form-courses.jsx
new file mode 100644
index 0000000000..e92154e8a9
--- /dev/null
+++ b/src/modules/signup/components/setup-form-courses.jsx
@@ -0,0 +1,50 @@
+import {
+ Checkbox,
+ CheckboxGroup,
+ Icon,
+ Input,
+ RadioGroup,
+ Row,
+ Select,
+ File,
+ Textarea,
+ ReactSelect,
+ Form
+} from '../../core/helpers/form';
+import {Link} from 'react-router';
+
+import Table from '../../administration/components/table';
+import AdminSectionCourses from '../../administration/containers/courses';
+
+class SetupFormCourses extends AdminSectionCourses {
+
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Administration:
Kurse
+
+
Sie können nun Kurse anlegen und diesen Lehrkräfte zuweisen.
+
"Kurse" bezeichnet sowohl Kurse in der Oberstufe, als auch Fächer für
+ jede Klasse der Mittelstufe (also zum Beispiel "Mathe für die 10a").
+
+
Tipp: Lehrer können auch selber Kurse anlegen.
+
+
+
+
+
+
+
+ );
+ }
+
+}
+
+export default SetupFormCourses;
+
+
+
diff --git a/src/modules/signup/components/setup-form-school.jsx b/src/modules/signup/components/setup-form-school.jsx
new file mode 100644
index 0000000000..314dc8f6fd
--- /dev/null
+++ b/src/modules/signup/components/setup-form-school.jsx
@@ -0,0 +1,75 @@
+import {
+ Checkbox,
+ CheckboxGroup,
+ Icon,
+ Input,
+ RadioGroup,
+ Row,
+ Select,
+ File,
+ Textarea,
+ ReactSelect,
+ Form
+} from '../../core/helpers/form';
+
+class SetupFormSchool extends React.Component {
+
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Informationen
über die Schule
+
+
Damit unser Team die Schule überprüfen und im Anschluss
+ freischalten kann werden ein paar Informationen benötigt.
+
+
+
+ );
+ }
+
+}
+
+export default SetupFormSchool;
+
+
+
diff --git a/src/modules/signup/components/setup-form-teachers.jsx b/src/modules/signup/components/setup-form-teachers.jsx
new file mode 100644
index 0000000000..a3b7ffcd10
--- /dev/null
+++ b/src/modules/signup/components/setup-form-teachers.jsx
@@ -0,0 +1,52 @@
+import {
+ Checkbox,
+ CheckboxGroup,
+ Icon,
+ Input,
+ RadioGroup,
+ Row,
+ Select,
+ File,
+ Textarea,
+ ReactSelect,
+ Form
+} from '../../core/helpers/form';
+import {Link} from 'react-router';
+
+import Table from '../../administration/components/table';
+import AdminSectionTeachers from '../../administration/containers/teachers';
+
+class SetupFormTeachers extends AdminSectionTeachers {
+
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
Administration:
Lehrkräfte
+
+
Ihr Account wurde freigeschaltet und die Schule freigegeben.
+ Sie können nun Lehrkräfte (Lehrer/innen, Betreuer, ...) anlegen.
+
+
Tipp: Sie können auch nach Abschluss der Registrierung
+ jeder Zeit weitere Lehrkräfte hinzufügen.
+
+
+
+
+
+
Im nächsten Schritt können Sie Klassen anlegen.
+
+
Fortsetzen
+
+ );
+ }
+
+}
+
+export default SetupFormTeachers;
+
+
+
diff --git a/src/modules/signup/components/setup.jsx b/src/modules/signup/components/setup.jsx
new file mode 100755
index 0000000000..5436e122bd
--- /dev/null
+++ b/src/modules/signup/components/setup.jsx
@@ -0,0 +1,52 @@
+import Layout from '../../core/components/layout'
+import SetupFormSchool from './setup-form-school';
+import SetupFormTeachers from './setup-form-teachers';
+import SetupFormClasses from './setup-form-classes';
+import SetupFormCourses from './setup-form-courses';
+
+require('../styles/signup.scss');
+
+class Setup extends React.Component {
+
+ constructor(props) {
+ super(props);
+ }
+
+ getFormStepUI() {
+ switch(this.props.step) {
+ case 'school': {
+ return
+ }
+ case 'teachers': {
+ return
+ }
+ case 'classes': {
+ return
+ }
+ case 'courses': {
+ return
+ }
+ default: {
+ // TODO: refactor to redirect to 404
+ return
Diese Seite existiert nicht.
+ }
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+ {this.getFormStepUI()}
+
+
+
+
+ );
+ }
+
+}
+
+export default Setup;
diff --git a/src/modules/signup/components/signup.jsx b/src/modules/signup/components/signup.jsx
new file mode 100644
index 0000000000..91e6f1ffa5
--- /dev/null
+++ b/src/modules/signup/components/signup.jsx
@@ -0,0 +1,169 @@
+import {
+ Input,
+ Form,
+ validators
+} from '../../core/helpers/form';
+import Layout from '../../core/components/layout';
+
+class Signup extends React.Component {
+
+ constructor(props) {
+ super(props);
+ }
+
+
+ getSharedFieldsUI() {
+ return (
+
+ );
+ }
+
+ getAdminFieldsUI() {
+ if(this.props.role !== 'admin') return;
+
+ return (
+
+ );
+ }
+
+ getPasswordFieldUI() {
+ if(this.props.isSSO) return;
+
+ return (
+
+
+
+
+ Ihr Passwort muss folgendes enthalten:
+
+
+ - Mindestens 8 Zeichen
+ - Groß- und Kleinschreibung
+ - Mindestens eine Zahl und Sonderzeichen
+
+
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+
+
Willkommen zur
Schul-Cloud
+
+
Im diesem Schritt würden wir Dich gerne besser kennen lernen:
+
+
+
+
+
+
+ );
+ }
+
+}
+
+export default Signup;
+
+
+
diff --git a/src/modules/signup/containers/setup.js b/src/modules/signup/containers/setup.js
new file mode 100644
index 0000000000..8b6798baf9
--- /dev/null
+++ b/src/modules/signup/containers/setup.js
@@ -0,0 +1,70 @@
+import { browserHistory } from 'react-router';
+import {render} from 'react-dom';
+import {compose} from 'react-komposer';
+import { SubsManager } from 'feathers-subscriptions-manager';
+
+import adminActions from '../../administration/actions/administration';
+
+import component from '../components/setup';
+import actions from '../actions/signup';
+
+
+import { Server } from '../../core/helpers';
+
+const pluckArrayToObject = (array, key) => {
+ const result = {};
+ array.forEach((obj) => {
+ result[obj[key]] = obj;
+ });
+ return result;
+};
+
+
+function composer(props, onData) {
+
+ const step = props.params.step;
+ const combinedActions = Object.assign({}, adminActions, actions);
+ const currentUser = Server.get('user');
+
+ // make sure that only allowed steps here
+ if(!['school', 'teachers', 'classes', 'courses'].includes(step)) {
+ throw new Error('not found', 404);
+ }
+
+ if(!currentUser) {
+ browserHistory.push('/login/');
+ return;
+ }
+
+ if((currentUser.preferences || {}).finishedSignup) {
+ browserHistory.push('/dashboard/');
+ return;
+ }
+
+ const schoolId = currentUser.schoolId;
+
+ const componentData = {
+ actions: combinedActions,
+ step,
+ onUpdateSchool: (data) => {
+ actions.updateSchool(data).then(() => {
+ browserHistory.push("/setup/teachers/");
+ });
+ },
+ onSignupFinished: () => {
+ actions.finishSetup(currentUser._id).then(() => {
+ browserHistory.push("/administration/");
+ });
+ }
+ };
+
+ Server.service('/schools').get(schoolId)
+ .then(school => {
+ Object.assign(componentData, {schoolId, school});
+ onData(null, componentData);
+ })
+ .catch(error => onData(error));
+
+}
+
+export default compose(composer)(component);
diff --git a/src/modules/signup/containers/signup.js b/src/modules/signup/containers/signup.js
new file mode 100644
index 0000000000..19a0b225b7
--- /dev/null
+++ b/src/modules/signup/containers/signup.js
@@ -0,0 +1,73 @@
+import { browserHistory } from 'react-router';
+import {render} from 'react-dom';
+import {compose} from 'react-komposer';
+import { SubsManager } from 'feathers-subscriptions-manager';
+
+const accountService = Server.service('/accounts');
+const userService = Server.service('/users');
+const schoolService = Server.service('/schools');
+import adminActions from '../../administration/actions/administration';
+
+import component from '../components/signup';
+import actions from '../actions/signup';
+
+import { Server } from '../../core/helpers';
+
+function composer(props, onData) {
+
+ let user = {};
+ let role = 'student';
+ let isSSO = false;
+ let accountId = '';
+ let schoolId = props.params.schoolId;
+ const recordId = props.params.recordId;
+ let dataPromises = [];
+
+ if(props.params.idType == 'a') {
+ // idType = accountId, used for student sso
+ if(!schoolId) {
+ throw new Error('schoolId has to be set if you want to use SSO.');
+ }
+
+ isSSO = true;
+ accountId = recordId;
+
+ // check that IDs are valid
+ dataPromises.push(schoolService.get(schoolId));
+ dataPromises.push(accountService.get(accountId));
+ } else if(props.params.idType == 'u') {
+ // idType = userId, used for teacher and admin
+ dataPromises.push(userService.get(recordId).then(data => {
+ user = data;
+ role = user.roles[0];
+ schoolId = user.schoolId;
+ }));
+ } else if(props.params.idType == 's') {
+ // idType = schoolId, used for students
+ schoolId = recordId;
+ }
+
+ Promise.all(dataPromises).then(_ => {
+ const componentData = Object.assign({
+ actions: Object.assign({}, adminActions, actions),
+ role,
+ user,
+ accountId,
+ schoolId,
+ isSSO,
+ onSignup: data => {
+ actions.signupUser(data).then(account => {
+ return actions.finishSetup(account.userId);
+ }).then(_ => {
+ browserHistory.push('/login/');
+ });
+ }
+ });
+
+ onData(null, componentData);
+ }).catch(() => {
+ onData(new Error('not authorized', 401));
+ });
+}
+
+export default compose(composer)(component);
diff --git a/src/modules/signup/index.js b/src/modules/signup/index.js
new file mode 100644
index 0000000000..02cf81e96b
--- /dev/null
+++ b/src/modules/signup/index.js
@@ -0,0 +1,5 @@
+import routes from './routes';
+
+export default {
+ routes
+};
diff --git a/src/modules/signup/routes.jsx b/src/modules/signup/routes.jsx
new file mode 100644
index 0000000000..7c902805ca
--- /dev/null
+++ b/src/modules/signup/routes.jsx
@@ -0,0 +1,16 @@
+import Signup from './containers/signup';
+import Setup from './containers/setup';
+
+export default [
+ {
+ path: '/signup/:idType/:recordId/(:schoolId/)', // schoolId is needed only for SSO
+ name: 'signup',
+ component: Signup
+ },
+ {
+ path: '/setup/:step/',
+ name: 'setup',
+ component: Setup
+ }
+];
+
diff --git a/src/modules/signup/styles/signup.scss b/src/modules/signup/styles/signup.scss
new file mode 100644
index 0000000000..e3c91b6c07
--- /dev/null
+++ b/src/modules/signup/styles/signup.scss
@@ -0,0 +1,16 @@
+@import '../../core/styles/colors.scss';
+
+.route-signup {
+ padding: 50px 0px;
+
+ h1 {
+ font-weight: 700;
+ color: #111;
+ margin-bottom:20px;
+ }
+
+ label {
+ font-weight: 700;
+ }
+
+}
diff --git a/src/modules/timetable/components/timetable.jsx b/src/modules/timetable/components/timetable.jsx
index eec1d206cb..885eecfb8b 100755
--- a/src/modules/timetable/components/timetable.jsx
+++ b/src/modules/timetable/components/timetable.jsx
@@ -1,5 +1,5 @@
-import LayoutBackend from '../../backend/containers/layout';
-import SectionTitle from '../../backend/components/title'; /* only for backend */
+import LayoutBase from '../../base/containers/layout';
+import SectionTitle from '../../base/components/title'; /* only for base */
import SectionTable from './table';
import SectionControls from './controls';
@@ -14,7 +14,7 @@ class Timetable extends React.Component {
render() {
return (
-
+
@@ -22,7 +22,7 @@ class Timetable extends React.Component {
-
+
);
}
diff --git a/src/modules/tools/actions/newTool.js b/src/modules/tools/actions/newTool.js
index a2b6176736..c9d4fde5da 100644
--- a/src/modules/tools/actions/newTool.js
+++ b/src/modules/tools/actions/newTool.js
@@ -1,19 +1,27 @@
import React from 'react';
import {browserHistory} from 'react-router';
-import { Server } from '../../core/helpers';
+import { Server, Notification } from '../../core/helpers';
-const toolsConnectService = Server.service('/ltiTools/connect');
const toolService = Server.service('/ltiTools');
+const courseService = Server.service('/courses');
export default {
createNew: (tool) => {
toolService.create(tool)
.then(result => {
- // Todo: remove when subsmanager is implemented
- window.location.href = '/tools/'
+ if (result._id) {
+ courseService.patch(tool.courseId, { $push: {ltiToolIds: result._id}}).then(result => {
+ browserHistory.push(`/courses/${result._id}`);
+ Notification.showSuccess("Neues Tool wurde erfolgreich angelegt!");
+ }).catch(err => {
+ Notification.showError(err.message);
+ return false;
+ });
+ }
})
.catch(err => {
- console.log(err);
+ Notification.showError(err.message);
+ return false;
});
}
};
diff --git a/src/modules/tools/actions/tools.js b/src/modules/tools/actions/tools.js
index 6c60f09635..6086c6ccee 100644
--- a/src/modules/tools/actions/tools.js
+++ b/src/modules/tools/actions/tools.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { Server, LTICustomer } from '../../core/helpers';
+import { Server, LTICustomer, Notification } from '../../core/helpers';
const rolesService = Server.service('/roles');
export default {
@@ -8,19 +8,19 @@ export default {
var consumer = LTICustomer.createConsumer(tool.key, tool.secret);
const currentUser = Server.get('user');
-
- // todo: do this better
rolesService.find({query: {'_id': currentUser.roles[0]}}).then((result) => {
let role = result.data[0].name;
var payload = {
lti_version: tool.lti_version,
lti_message_type: tool.lti_message_type,
- resource_link_id: tool.resource_link_id,
+ resource_link_id: tool.courseId || tool.resource_link_id,
user_id: currentUser._id,
roles: LTICustomer.mapSchulcloudRoleToLTIRole(role),
launch_presentation_document_target: 'window',
- lis_person_name_full: 'John Logie Baird', // todo: get from user, wait for populate
- lis_person_contact_email_primary: 'jbaird@uni.ac.uk',
+ lis_person_name_full: currentUser.firstName && currentUser.lastName
+ ? `${currentUser.firstName} ${currentUser.lastName}`
+ : 'John Logie Baird',
+ lis_person_contact_email_primary: currentUser.username ? `${currentUser.username}@schul-cloud.org` : 'jbaird@uni.ac.uk',
launch_presentation_locale: 'en'
};
@@ -35,6 +35,9 @@ export default {
};
LTICustomer.sendRequest(request_data, consumer);
+ }).catch(err => {
+ Notification.showError(err.message);
+ return false;
});
}
};
diff --git a/src/modules/tools/components/newTool.jsx b/src/modules/tools/components/newTool.jsx
index 10d9c8efe6..29c0500dbc 100644
--- a/src/modules/tools/components/newTool.jsx
+++ b/src/modules/tools/components/newTool.jsx
@@ -1,5 +1,5 @@
-import LayoutBackend from '../../backend/containers/layout';
-import SectionTitle from '../../backend/components/title'; /* only for backend */
+import LayoutBase from '../../base/containers/layout';
+import SectionTitle from '../../base/components/title'; /* only for base */
import {browserHistory} from 'react-router';
import TemplateToolCard from './templateToolCard';
@@ -14,17 +14,17 @@ class NewTool extends React.Component {
render() {
let idCount = 0;
return (
-
+
{
this.props.tools.map((tool) => {
idCount++;
- return ;
+ return ;
})
}
-
+
);
}
diff --git a/src/modules/tools/components/newToolForm.jsx b/src/modules/tools/components/newToolForm.jsx
index f4aabbce0e..53e65a5cd0 100644
--- a/src/modules/tools/components/newToolForm.jsx
+++ b/src/modules/tools/components/newToolForm.jsx
@@ -1,5 +1,8 @@
-import LayoutBackend from '../../backend/containers/layout';
-import SectionTitle from '../../backend/components/title';
+import {
+ Input,
+ Select,
+ Form
+} from '../../core/helpers/form';
require('../styles/newToolForm.scss');
@@ -19,7 +22,8 @@ class NewToolForm extends React.Component {
}
};
- this.handleChange = this.handleChange.bind(this);
+ // set default course
+ this.state.tool.courseId = this.props.courses[0]._id;
this.handleSubmit = this.handleSubmit.bind(this);
}
@@ -27,14 +31,7 @@ class NewToolForm extends React.Component {
return key + value + Math.random() * 10000;
}
- handleChange(fieldName, event) {
- var stateUpdate = this.state.tool;
- stateUpdate[fieldName] = event.target.value;
- this.setState({tool: stateUpdate});
- }
-
handleSubmit(event) {
- event.preventDefault();
var tool = this.state.tool;
tool.customs = this.state.custom_fields;
@@ -66,7 +63,7 @@ class NewToolForm extends React.Component {
this.setState({custom_fields: stateCustomsUpdate});
}
- renderNewCustomFields() {
+ renderNewCustomField() {
return (
@@ -92,34 +89,61 @@ class NewToolForm extends React.Component {
)
}
+ getCourseOptions() {
+ return this.props.courses.map(c => {
+ return {label: c.name, value: c._id};
+ });
+ }
+
+ getPrivacyOptions() {
+ return [
+ {label: "Nur E-Mail", value: "e-mail"},
+ {label: "Nur Name", value: "name"},
+ {label: "Öffentlich", value: "public"}
+ ]
+ }
+
render() {
return (
-
+
+
);
}
diff --git a/src/modules/tools/components/templateToolCard.jsx b/src/modules/tools/components/templateToolCard.jsx
index 258314da4b..52f6eb6e0f 100644
--- a/src/modules/tools/components/templateToolCard.jsx
+++ b/src/modules/tools/components/templateToolCard.jsx
@@ -35,7 +35,7 @@ class ToolCard extends React.Component {
Neues LTI-Tool erstellen
-
+