diff --git a/.dockerignore b/.dockerignore
index 979bf6b132087e22b4d76d2bd03294bf8b5b7134..28ec9068be8d0696b56ed5d15daf343902a034a2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -11,4 +11,4 @@ coverage
.grunt
.lock-wscript
build/Release
-node_modules
\ No newline at end of file
+node_modules
diff --git a/.gitignore b/.gitignore
index a21677383a98825c37911974586821109b7c0a67..600a344211eb756d858efc918785035ed07f6349 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
node_modules
javascripts/maps
javascripts/spacedeck.js
+public/stylesheets/*.css
+database.sqlite
*.swp
*~
diff --git a/Dockerfile b/Dockerfile
index a614e973b8f137e66ae4c280ba5fc14363fcb4e6..2c486ff55310aabfa5328136592cec6d6817f4c2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,29 +1,38 @@
-FROM spacedeck/docker-baseimage:latest
-ENV NODE_ENV production
+FROM node:10-alpine3.11
-RUN mkdir -p /usr/src/app
-WORKDIR /usr/src/app
+WORKDIR /app
-COPY package.json /usr/src/app/
-RUN npm install
-RUN npm install gulp-rev-replace gulp-clean gulp-fingerprint gulp-rev gulp-rev-all gulp-rev-replace
-RUN npm install -g --save-dev gulp
+# build audiowaveform from source
+
+RUN apk add git make cmake gcc g++ libmad-dev libid3tag-dev libsndfile-dev gd-dev boost-dev libgd libpng-dev zlib-dev
+RUN apk add zlib-static libpng-static boost-static
+
+RUN apk add autoconf automake libtool gettext
+RUN wget https://github.com/xiph/flac/archive/1.3.3.tar.gz
+RUN tar xzf 1.3.3.tar.gz
+RUN cd flac-1.3.3/ && ./autogen.sh
+RUN cd flac-1.3.3/ && ./configure --enable-shared=no
+RUN cd flac-1.3.3/ && make
+RUN cd flac-1.3.3/ && make install
+
+RUN git clone https://github.com/bbc/audiowaveform.git
+RUN mkdir audiowaveform/build/
+RUN cd audiowaveform/build/ && cmake -D ENABLE_TESTS=0 -D BUILD_STATIC=1 ..
+RUN cd audiowaveform/build/ && make
+RUN cd audiowaveform/build/ && make install
-COPY app.js Dockerfile Gulpfile.js LICENSE /usr/src/app/
-COPY config /usr/src/app/config
-COPY helpers /usr/src/app/helpers
-COPY locales /usr/src/app/locales
-COPY middlewares /usr/src/app/middlewares
-COPY models /usr/src/app/models
-COPY public /usr/src/app/public
-COPY routes /usr/src/app/routes
-COPY styles /usr/src/app/styles
-COPY views /usr/src/app/views
+# install other requirements
-RUN gulp all
-RUN npm cache clean
+RUN apk add graphicsmagick ffmpeg ffmpeg-dev ghostscript
+
+# install node package
+
+COPY package*.json ./
+RUN npm install
+COPY . .
-CMD [ "node", "app.js" ]
+# start app
EXPOSE 9666
+CMD ["node", "spacedeck.js"]
diff --git a/Gulpfile.js b/Gulpfile.js
index ee34f4dcfc56acf44de46ba16dad4de96982a3c7..f20bf7ee4326e6eb7b6ff7bb0814187c14cbf57c 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -1,13 +1,13 @@
-var gulp = require('gulp');
-var sass = require('gulp-sass');
-var concat = require('gulp-concat');
+const gulp = require('gulp')
+const sass = require('gulp-sass')
+const concat = require('gulp-concat')
-gulp.task('styles', function() {
+gulp.task('styles', function(done) {
gulp.src('styles/**/*.scss')
.pipe(sass({
errLogToConsole: true
}))
.pipe(gulp.dest('./public/stylesheets/'))
- .pipe(concat('style.css'));
-});
-
+ .pipe(concat('style.css'))
+ done()
+})
diff --git a/README.md b/README.md
index 10c2e3f2d3cda8aaa31701a8a3f87e29ac5f63c7..a70e100fb52f2a0071c68fa247eff7b876b38946 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,13 @@
# Spacedeck Open
+![Spacedeck 6.0 Screenshot](/public/images/sd6-screenshot.png)
+
This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin Güther (magegu).
The spacedeck.com online service was shut down on May 1st 2018. We decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted or local version.
+[MNT Research GmbH](https://mntre.com) has restarted development of Spacedeck Open in 2020.
+
We appreciate filed issues, pull requests and general discussion.
# Features
@@ -13,9 +17,9 @@ We appreciate filed issues, pull requests and general discussion.
- Write and format text with full control over fonts, colors and style
- Draw, annotate and highlight with included graphical shapes
- Turn your Space into a zooming presentation
-- Collaborate and chat in realtime with teammates, students or friends
+- Collaborate in realtime with teammates, students or friends
- Share Spaces on the web or via email
-- Export your work as printable PDF or ZIP
+- Export your work as printable PDF or ZIP (currently being fixed, stay tuned)
# Use Cases
@@ -23,23 +27,15 @@ We appreciate filed issues, pull requests and general discussion.
- Creative: Mood boards, Brainstorming, Design Thinking
- Visual note taking and planning
-# Data Import from Spacedeck.com
-
-Spacedeck Open has a data import feature that you can use to migrate your ZIP export from Spacedeck.com.
-
-1. Just copy your downloaded ZIP file into the spacedeck root folder. Don't extract it.
-2. Start your local Spacedeck.
-3. Navigate to *Account / Profile* (person icon in the top right corner).
-4. Click the *Import* button next to the ZIP file name. It is on the bottom of the page.
-5. Wait until console output has finished and you're done.
-
# Requirements, Installation
Spacedeck requires:
-- Node.js 9.x: Web Server / API. Download: https://nodejs.org
+- Node.js 10.x: Web Server / API. Download: https://nodejs.org
+- Graphicsmagick. On non-Linux, Download: http://www.graphicsmagick.org/ On Linux, install via package manager.
+- Optionally ffmpeg, audiowaveform and ghostscript. See "Optional Dependencies" below.
-To run Spacedeck, you only need Node.JS 9.x.
+To run Spacedeck, you only need Node.JS 10.x.
To install all node dependencies, run (do this once):
@@ -47,7 +43,7 @@ To install all node dependencies, run (do this once):
# Configuration
-See [config/default.json](config/default.json)
+See [config/default.json](config/default.json). Set `storage_local_path` for a local sqlite database or `storage_region`, `storage_bucket`, `storage_cdn` and `storage_endpoint` for AWS S3. `mail_provider` may be one of `console` or `smtp`. Also, omit a trailing `/` for the `endpoint`.
# Run (web server)
@@ -55,10 +51,6 @@ See [config/default.json](config/default.json)
Then open http://localhost:9666 in a web browser.
-# Run (desktop app with integrated web server)
-
- electron .
-
# Optional Dependencies
For advanced media conversion:
@@ -72,6 +64,16 @@ For advanced media conversion:
By default, media files are uploaded to the ```storage``` folder.
The database is stored in ```database.sqlite``` by default.
+# Run with Docker
+
+- configure `config/default.json`
+- configure `volumes` section inside `docker-compose.yml`
+ - point to `database.sqlite` on the host system
+ - `touch database.sqlite` if it not exists
+ - point to `storage/` on the host system
+ - `mkdir storage/` if it not exists
+- start the container with `sudo docker-compose up -f docker-compose.yml -d --build`
+
# Hacking
To rebuild the frontend CSS styles:
diff --git a/app.js b/app.js
deleted file mode 100644
index 73d7fd576777f7944529a3045f31eacc1cf2ecd8..0000000000000000000000000000000000000000
--- a/app.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const spacedeck = require('./spacedeck')
-
-const electron = require('electron')
-const electronApp = electron.app
-const BrowserWindow = electron.BrowserWindow
-let mainWindow
-
-function createWindow () {
- mainWindow = new BrowserWindow({width: 1200, height: 700})
- mainWindow.loadURL("http://localhost:9666")
- mainWindow.on('closed', function () {
- mainWindow = null
- })
-}
-
-electronApp.on('ready', createWindow)
-
-// Quit when all windows are closed.
-electronApp.on('window-all-closed', function () {
- // On OS X it is common for applications and their menu bar
- // to stay active until the user quits explicitly with Cmd + Q
- if (process.platform !== 'darwin') {
- electronApp.quit()
- }
-})
-
-electronApp.on('activate', function () {
- // On OS X it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
- if (mainWindow === null) {
- createWindow()
- }
-})
diff --git a/bin/www b/bin/www
deleted file mode 100755
index a1d997361b78dc62e1a97dea03d4e21a211e6dc2..0000000000000000000000000000000000000000
--- a/bin/www
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env node
-
-var app = require('../app');
-var http = require('http');
-var server = http.createServer(app);
diff --git a/config/default.json b/config/default.json
index 04ff126adf9434450e8f9770a3a225cba566335e..cd012ee147ca9726b86ab669c2270ca95a6fac18 100644
--- a/config/default.json
+++ b/config/default.json
@@ -2,16 +2,17 @@
"team_name": "My Open Spacedeck",
"contact_email": "support@example.org",
+ "host": "::",
+ "port": 9666,
"endpoint": "http://localhost:9666",
+ "invite_code": "top-sekrit",
"storage_region": "eu-central-1",
- //"storage_bucket": "sdeck-development",
- //"storage_cdn": "http://localhost:9123/sdeck-development",
- //"storage_endpoint": "http://storage:9000",
"storage_bucket": "my_spacedeck_bucket",
"storage_cdn": "/storage",
"storage_local_path": "./storage",
+ "storage_local_db": "./database.sqlite",
"redis_mock": true,
"mongodb_host": "localhost",
@@ -22,12 +23,15 @@
"admin_pass": "very_secret_admin_password",
"phantom_api_secret": "very_secret_phantom_password",
- // Choose "console" or "smtp"
"mail_provider": "smtp",
"mail_smtp_host": "your.smtp.host",
"mail_smtp_port": 465,
"mail_smtp_secure": true,
"mail_smtp_require_tls": true,
"mail_smtp_user": "your.smtp.user",
- "mail_smtp_pass": "your.secret.smtp.password"
+ "mail_smtp_pass": "your.secret.smtp.password",
+
+ "path" : "http://localhost:9666/saml/SSO",
+ "entryPoint" : "https://m4lab.hft-stuttgart.de/idp/saml2/idp/SSOService.php",
+ "issuer" : "spacedeck.m4lab.hft-stuttgart.de"
}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4823997f2da9615aae05cb0a75de713b173a614f
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,12 @@
+version: "2.0"
+
+services:
+ spacedeck:
+ build: .
+ container_name: spacedeck
+ ports:
+ - "9666:9666"
+ volumes:
+ - /absolute/path/to/storage:/app/storage
+ - /absolute/path/to/database.sqlite:/app/database.sqlite
+
diff --git a/docs/adding_fonts.md b/docs/adding_fonts.md
new file mode 100644
index 0000000000000000000000000000000000000000..dbad8895e14fc683084d962c7759ef0d888db874
--- /dev/null
+++ b/docs/adding_fonts.md
@@ -0,0 +1,6 @@
+To add fonts to Spacedeck Open, follow these steps:
+
+1. Find the googleapis link for the font and add it to [./styles/type.scss](https://github.com/spacedeck/spacedeck-open/blob/docs/styles/type.scss#L4) after the `Inter` font that is already there. Here is a good reference to using [Google Font API](https://www.webfx.com/blog/web-design/google-font-api-guide/).
+2. Add the name of the font to the file [./public/javascripts/spacedeck_sections.js](https://github.com/spacedeck/spacedeck-open/blob/docs/public/javascripts/spacedeck_sections.js#L150) in the `fonts` section found around line 150. The order of the list here is the order in which fonts will be displayed in the user interface.
+3. From the root of your install, do `gulp styles` to recompile the SCSS.
+4. Restart your server.
diff --git a/docs/adding_languages.md b/docs/adding_languages.md
new file mode 100644
index 0000000000000000000000000000000000000000..490323bf5cf697a12b8bd88625112b9c3d3b6fa3
--- /dev/null
+++ b/docs/adding_languages.md
@@ -0,0 +1,26 @@
+## Adding a new language to Spacedeck Open
+
+To add a new language to Spacedeck Open, follow these steps:
+
+*The steps are illustrated with Spanish (locale 'es') as the new language*
+
+- Include the new locale ('es') in the locale list (./spacedeck.js):
+```
+ locales: ["en",..., "es"],
+```
+- Create the new translation file (/locales/**es.js**, a copy of /locales/en.js) and translate the entries.
+- Include the javascript for the new translation at the end of /views/spacedeck.ejs:
+
+ ```
+ ...
+ window.locales.es = {};
+ ...
+ window.locales.es.translation = <%- include "./../locales/es.js" %>;
+
+ ```
+- Include a radio button for users to select the new language (/views/partials/account.html)
+ ```
+
+ ```
diff --git a/electron-windows.md b/electron-windows.md
deleted file mode 100644
index de6a8700aac60f5b295d3c594b102b71ac36c29b..0000000000000000000000000000000000000000
--- a/electron-windows.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-# Windows Electron Build
-
-sqlite3 needs to be manually built for the iojs version that electron ships. The following code assumes electron v1.8.4.
-
-````
-npm -g install windows-build-tools
-
-cd node_modules\sqlite3
-
-node-gyp configure --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64
-
-node-gyp rebuild --target=1.8.4 --target_platform=win32 --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64 --msvs_version=2015
-
-cd ..\..
-````
-
diff --git a/helpers/mailer.js b/helpers/mailer.js
index 39a0a3c2c55b4ca8ab2eda067043f7b352ba4ec2..74f73590d4f186806b326eada6c74c95729ddf4f 100644
--- a/helpers/mailer.js
+++ b/helpers/mailer.js
@@ -2,8 +2,6 @@
const config = require('config');
const nodemailer = require('nodemailer');
-const swig = require('swig');
-//var AWS = require('aws-sdk');
module.exports = {
sendMail: (to_email, subject, body, options) => {
@@ -24,35 +22,38 @@ module.exports = {
plaintext+="\n"+options.action.link+"\n\n";
}
- const htmlText = swig.renderFile('./views/emails/action.html', {
- text: body.replace(/(?:\n)/g, ' '),
- options: options
- });
-
if (config.get('mail_provider') === 'console') {
- console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext);
+ console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + plaintext + "\n\n plaintext:\n" + plaintext);
} else if (config.get('mail_provider') === 'smtp') {
-
- const transporter = nodemailer.createTransport({
- host: config.get('mail_smtp_host'),
- port: config.get('mail_smtp_port'),
- secure: config.get('mail_smtp_secure'),
- requireTLS: config.get('mail_smtp_require_tls'),
- auth: {
- user: config.get('mail_smtp_user'),
- pass: config.get('mail_smtp_pass'),
- }
- });
+ let transporter;
+ if (config.has('mail_smtp_user')) {
+ transporter = nodemailer.createTransport({
+ host: config.get('mail_smtp_host'),
+ port: config.get('mail_smtp_port'),
+ secure: config.get('mail_smtp_secure'),
+ requireTLS: config.get('mail_smtp_require_tls'),
+ auth: {
+ user: config.get('mail_smtp_user'),
+ pass: config.get('mail_smtp_pass'),
+ }
+ });
+ } else {
+ transporter = nodemailer.createTransport({
+ host: config.get('mail_smtp_host'),
+ port: config.get('mail_smtp_port'),
+ secure: config.get('mail_smtp_secure'),
+ requireTLS: config.get('mail_smtp_require_tls'),
+ });
+ }
transporter.sendMail({
from: from,
replyTo: reply_to,
to: to_email,
subject: subject,
- text: plaintext,
- html: htmlText,
+ text: plaintext
}, function(err, info) {
if (err) {
console.error("Error sending email:", err);
@@ -61,33 +62,6 @@ module.exports = {
}
});
- } else if (config.get('mail_provider') === 'aws') {
- /*
- AWS.config.update({region: 'eu-west-1'});
- var ses = new AWS.SES();
-
- ses.sendEmail( {
- Source: from,
- Destination: { ToAddresses: [to_email] },
- ReplyToAddresses: reply_to,
- Message: {
- Subject: {
- Data: subject
- },
- Body: {
- Text: {
- Data: plaintext,
- },
- Html: {
- Data: htmlText
- }
- }
- }
- }, function(err, data) {
- if (err) console.error("Error sending email:", err);
- else console.log("Email sent.");
- });
- */
}
}
};
diff --git a/integrations/wordpress/plugins/spacedeck/spacedeck.php b/integrations/wordpress/plugins/spacedeck/spacedeck.php
new file mode 100644
index 0000000000000000000000000000000000000000..97210aa37827abc3fc150241adcf431bc94a9b0d
--- /dev/null
+++ b/integrations/wordpress/plugins/spacedeck/spacedeck.php
@@ -0,0 +1,202 @@
+ 'application/json',
+ 'X-Spacedeck-API-Token' => $spacedeck_api_key
+ );
+
+ $payload = array(
+ 'method' => $method,
+ 'timeout' => 10,
+ 'blocking' => true,
+ 'headers' => $headers,
+ 'body' => $data_string
+ );
+
+ // echo("
Spacedeck: Error embedding Space. Is your API key set up correctly?
");
+ }
+
+ $space_auth = $space[edit_hash];
+
+ // return a piece of html (iframe) embedding the space
+ $uri = $spacedeck_frontend_base_uri . '/spaces/' . $slug . '?embedded=1&spaceAuth=' . $space_auth;
+
+ $html = "";
+
+ return $html;
+}
+
+function spacedeck_shortcode($attrs) {
+ extract(shortcode_atts(array(
+ 'id' => 'none',
+ 'parent_space_id' => null,
+ 'width' => '100%',
+ 'height' => '800'
+ ), $attrs));
+
+ $w = $attrs[width];
+ $h = $attrs[height];
+ if (!$w) $w = '100%';
+ if (!$h) $h = 800;
+
+ return spacedeck_embed_space($attrs[id],$w,$h,$attrs[parent_space_id]);
+}
+
+add_shortcode('spacedeck_space', 'spacedeck_shortcode');
+
+add_action('admin_menu', 'spacedeck_add_admin_menu');
+add_action('admin_init', 'spacedeck_settings_init');
+
+function spacedeck_add_admin_menu() {
+ add_options_page('spacedeck', 'Spacedeck', 'manage_options', 'spacedeck', 'spacedeck_options_page');
+}
+
+function spacedeck_settings_init() {
+ register_setting('pluginPage', 'spacedeck_settings');
+
+ add_settings_section(
+ 'spacedeck_pluginPage_section',
+ 'Spacedeck Settings',
+ 'spacedeck_settings_section_callback',
+ 'pluginPage'
+ );
+
+ add_settings_field(
+ 'spacedeck_text_field_0',
+ 'API key',
+ 'spacedeck_text_field_0_render',
+ 'pluginPage',
+ 'spacedeck_pluginPage_section'
+ );
+
+ add_settings_field(
+ 'spacedeck_text_field_1',
+ 'API base URL',
+ 'spacedeck_text_field_1_render',
+ 'pluginPage',
+ 'spacedeck_pluginPage_section'
+ );
+
+ add_settings_field(
+ 'spacedeck_text_field_2',
+ 'Frontend base URL',
+ 'spacedeck_text_field_2_render',
+ 'pluginPage',
+ 'spacedeck_pluginPage_section'
+ );
+}
+
+function spacedeck_text_field_0_render() {
+ $opts = get_option('spacedeck_settings');
+ ?>
+
+
+
+
+
+
+
+
diff --git a/locales/es.js b/locales/es.js
new file mode 100644
index 0000000000000000000000000000000000000000..b51ebf6b97f53dc89af1fdb0b4744b29a1489343
--- /dev/null
+++ b/locales/es.js
@@ -0,0 +1,324 @@
+{
+ "ok": "OK",
+ "cancel": "Cancelar",
+ "close": "Cerrar",
+ "open": "Abrir",
+ "folder": "Directorio",
+ "save": "Salvar",
+ "saved": "Salvado",
+ "created": "creado",
+ "duplicate": "Duplicar",
+ "delete": "Borrar",
+ "remove": "Eliminar",
+ "set": "ajustar",
+ "reset": "reiniciar",
+ "thanks": "Gracias",
+ "share": "Compartir",
+ "signup": "Regístrate",
+ "login": "Iniciar sesión",
+ "logout": "Cerrar sesión",
+ "email": "Correo Electrónico",
+ "password": "Contraseña",
+ "width": "Anchura",
+ "height": "Altura",
+ "nick": "Nombre",
+ "role": "Rol",
+ "members": "Miembros",
+ "actions": "Acciones",
+ "or": "o",
+ "you": "tú",
+ "via": "via",
+ "by": "por",
+ "zero": "Cero",
+ "page": "Página",
+ "new": "Nuevo",
+ "copy": "Copiar",
+ "home": "Inicio",
+ "owner": "Propietario",
+ "space": "Espacio",
+ "second": "Segundo",
+ "not_found": "No encontrado.",
+ "untitled_space": "Espacio sin título",
+ "untitled_folder": "Directorio sin título",
+ "untitled": "sin título",
+ "sure": "Está seguro?",
+ "specify": "Por favor, Especifica",
+ "confirm": "Por favor, Confirma",
+ "error_unknown_email": "Esta combinación correo electrónico/contraseña no es conocida.",
+ "error_password_confirmation": "La contraseña introducida no coincide.",
+ "error_domain_blocked": "Tu dominio está bloqueado.",
+ "error_user_email_already_used": "Esta dirección de correo electrónico ya se está usando.",
+ "support": "Soporte para Spacedeck",
+ "offline": "Offline. Clica para más.",
+ "error": "Lo siento, pero algo salió mal. Por favor, contacta con support@spacedeck.com",
+ "welcome": "Bienvenido",
+ "claim": "Tu Pizarra digital.",
+ "trynow": "Inténtalo ahora.",
+ "about": "Sobre nosotros.",
+ "terms": "Términos",
+ "contact": "Contacto",
+ "privacy": "Privacidad",
+ "business_adress": "Dirección de Negocios",
+ "post_adress": "Dirección postal",
+ "phone": "Teléfono",
+ "ceo": "Director gerente",
+ "name": "Nombre",
+ "confirm_subject": "Correo electrónico de confirmación de Spacedeck",
+ "confirm_body": "Gracias por iniciar sesión en Spacedeck.\nPor favor, clica en el siguiente enlace para confirmar tu dirección de correo electrónico.\n",
+ "confirm_action": "Confirmar Ahora",
+ "team_invite_membership_subject": "Inivitación de equipo para %s",
+ "team_invite_membership_body": "Has sido invitado a %s en Spacedeck. Por favor, clica en el siguiente enlace para aceptar la invitación.",
+ "team_invite_user_body": "Has sido invitado a %s en Spacedeck.\nTu contraseña temporal es \"%s\".\nPor favor, clica en el siguiente enlace para aceptar la invitación.",
+ "team_invite_admin_body": "%s fue invitado tu equipo: %s. La contraseña temporal es \"%s\".",
+ "team_invite_membership_acction": "Aceptar",
+ "team_new_member_subject": "Un nuevo Miembro para el Equipo %s se ha registrado",
+ "team_new_member_body": "%s se acaba de unir al Equipo %s en Spacedeck.",
+ "space_invite_membership_subject": "%s te invitó al Espacio %s ",
+ "space_invite_membership_body": "Has sido invitado por %s para unirte al Espacio %s en Spacedeck. Por favor, clica en el siguiente enlace para aceptar la invitación.",
+ "space_invite_membership_action": "Aceptar",
+ "folder_invite_membership_subject": "Espacio",
+ "folder_invite_membership_body": "Has sido invitado a un Equipo en Spacedeck. Por favor, clica en el siguiente enlace para aceptar la invitación.",
+ "folder_invite_membership_acction": "Aceptar",
+ "login_google": "Iniciar sesión con Google",
+ "save_changes": "Salvar Cambios",
+ "upgrade": "Mejorar",
+ "upgrade_now": "Mejorar ahora",
+ "create_space": "Crear Espacio",
+ "create_folder": "Crear Directorio",
+ "email_unconfirmed": "Correo electrónico no confirmado",
+ "confirmation_sent": "Correo electrónico enviado",
+ "folder_filter": "Filtro",
+ "sort_by": "Ordenar por",
+ "last_modified": "Última Modificación",
+ "last_opened": "Última Apertura",
+ "title": "Título",
+ "edit_team": "Editar Equipo",
+ "edit_account": "Edit Cuenta",
+ "log_out": "Cerrar Sesión",
+ "no_spaces_yet": "¡Bienvenido! Puedes crear Espacios y Directorios aquí utilizando los botones que se encuentran en la esquina superior izquierda.",
+ "new_folder_title": "Nuevo título para el directorio",
+ "folder_settings": "Ajustes de Directorio",
+ "upload_cover_image": "Cargar imagen de cubierta",
+ "spacedeck_pro_ad_folders": "Con Spacedeck Pro, puedes organizar un ilimitado número de Espacios y Directorios, y gestionar el control de acceso para cada Directorio. ¿Te gustaría aprender más sobre las características Pro?",
+ "spacedeck_pro_ad_versions": "Con Spacedeck Pro, puedes organizar un ilimitado número de versiones para cada Espacio así como realizar un seguimiento de su progreso o mantener instantáneas ('snapshots') seguras. ¿Te gustaría aprender más sobre las características Pro?",
+ "spacedeck_pro_ad_pdf": "Con Spacedeck Pro, puedes exportar tus Espacios como PDFs para su archivo, envío por correo, o impresión. ¿Te gustaría aprender más sobre las características Pro?",
+ "spacedeck_pro_ad_zip": "Con Spacedeck Pro, puedes exportar los contenidos de un Espacio empaquetado como un fichero ZIP. ¿Te gustaría aprender más sobre las características Pro?",
+ "spacedeck_pro_ad_colors": "Con Spacedeck Pro, puedes puedes usar tus propios colores usando un selector de color profesional.",
+ "profile_caption": "Perfil",
+ "upload_avatar": "Cardar Avatar",
+ "uploading_avatar": "Cargando Avatar…",
+ "avatar_dimensions": "Dimensiones recomendadas: 200×200 pixels.",
+ "profile_name": "Nombre",
+ "profile_email": "Dirección de correo electrónico",
+ "send_again": "Enviar de nuevo",
+ "confirmation_sent_long": "Correo electrónico con enlace de confirmación enviado. Por favor, revisa tu bandeja de entrada de Correo.",
+ "confirmation_sent_another": "Otro enlace de confirmación enviado.",
+ "confirmation_sent_dialog_text": "Te hemos enviado un correo explicando como confirmar tu dirección de correo electrónico.",
+ "payment_caption": "Pago",
+ "language_caption": "Idioma",
+ "notifications_caption": "Notificaciones",
+ "notifications_option_chat": "Infórmame via correo electrónico sobre nuevos cometarios",
+ "notifications_option_spaces": "Envíame un resumen diario de lo que sucedió en mis Espacios y Directorios.",
+ "password_caption": "Contraseña",
+ "current_password": "Contraseña Actual",
+ "new_password": "Nueva Contraseña",
+ "verify_password": "Verificar Contraseña",
+ "change_password": "Cambiar Contraseña",
+ "reset_password": "Reiniciar Contraseña",
+ "terminate_caption": "Borrar Cuenta",
+ "terminate_warning": "Si borras tu cuenta, todos los Espacios, Directorios y Mensajes (incluyendo todo el contenido que tú y otras personas crearon en tus Espacios) serán destruidos.",
+ "terminate_warning2": "Esta acción no puede deshacerse.",
+ "terminate_reason": "Mensaje",
+ "terminate_reason_caption": "Ayúdannos a mejorar compartiendo las razones por las que cancelas la cuenta.",
+ "terminate_terminate": "Terminar",
+ "space_blank1": "¡Bienvenido a un nuevo Espacio en blanco!",
+ "space_blank2": "Suelta ficheros, pega enlaces",
+ "space_blank3": "o utilizar las herramientas que aparecen abajo",
+ "space_blank4": "para rellenar este Espacio con contenido.",
+ "draft": "Borrador",
+ "publish": "Publicar",
+ "published": "Publicado",
+ "save_version": "Salvar Versión",
+ "version_saved": "Versión Salvada",
+ "post": "Publicar mensaje",
+ "chat_invite_cta1": "¡La Collaboración es divertida!",
+ "chat_invite_cta2": "¿Por qué no ",
+ "chat_invite_cta3": "invitar a algunas personas",
+ "chat_invite_cta4": "a trabajar contigo?",
+ "chat_message_placeholder": "Escribe tu mensaje…",
+ "view": "Ver",
+ "edit": "Editar",
+ "present": "Presentar",
+ "chat": "Chatear",
+ "meta": "Metadatos",
+ "tool_search": "Buscar",
+ "tool_upload": "Cargar",
+ "tool_text": "Texto",
+ "tool_shape": "Dar forma",
+ "tool_zones": "Zonas",
+ "tool_canvas": "Fondo pizarra",
+ "search_media": "Buscar multimedia…",
+ "type_here": "Escriba aquí",
+ "text_formats": "Formatos",
+ "format_p": "Párrafos",
+ "format_bullets": "Lista con 'Bullets'",
+ "format_numbers": "Lista Numérica",
+ "format_h1": "Titular 1",
+ "format_h2": "Titular 2",
+ "format_h3": "Titular 3",
+ "font_size": "Tamaño de Fuente",
+ "line_height": "Altura de la Línea",
+ "tool_align": "Alinear",
+ "tool_styles": "Estilos",
+ "tool_bullets": "'Bullets'",
+ "tool_numbers": "Números",
+ "color_fill": "Rellenar",
+ "color_stroke": "Trazo",
+ "color_text": "Texto",
+ "tool_type": "Tipo",
+ "tool_box": "Caja",
+ "tool_link": "Enlace",
+ "tool_layout": "Disposición",
+ "tool_options": "Opciones",
+ "tool_stroke": "Trazar",
+ "tool_delete": "Borrar",
+ "tool_lock": "Bloquear",
+ "tool_copy": "Copiar",
+ "stack": "Apilar",
+ "tool_circle": "Círculo",
+ "tool_hexagon": "Hexágono",
+ "tool_square": "Cuadrado",
+ "tool_diamond": "Diamante",
+ "tool_bubble": "Burbuja",
+ "tool_cloud": "Nube",
+ "tool_burst": "Ráfaga",
+ "tool_star": "Estrella",
+ "tool_heart": "Corazón",
+ "tool_scribble": "Garabatear",
+ "tool_line": "Líneas",
+ "tool_arrow": "Flecha",
+ "search_media_placeholder": "Buscar multimedia en web…",
+ "add_zone": "Nueva Zona",
+ "palette": "Paleta",
+ "picker": "Selector",
+ "background_image_caption": "Imagen",
+ "background_color_caption": "Color",
+ "upload_background_caption": "Clica para cargar una imagen de fondo",
+ "upload_background": "Cargar Fundo",
+ "access_caption": "Acceso",
+ "versions_caption": "Versiones",
+ "info_caption": "Información",
+ "mode_private": "Privado: Solo miembros pueden visualizar o editar",
+ "mode_public": "Público: Cualquiera con el enlace puede visualizar",
+ "invite_collaborators": "Invitar Colaboradores",
+ "revoke_access": "Anular Acceso",
+ "invite": "Enviar Invitaciones",
+ "invitee_email_address": "Dirección de correo electrónico del nuevo miembro",
+ "optional_message": "Mensaje optional",
+ "role_viewer": "Visualizador",
+ "role_editor": "Editor",
+ "role_admin": "Administrador",
+ "new_space_title": "Nuevo título para el Espacio",
+ "team": "Equipo",
+ "search": "Buscar",
+ "search_no_results": "Búsqueda sin resultados",
+ "search_clear": "Limpiar búsqueda",
+ "rename": "Renombrar",
+ "mobile": "teléfono móvl",
+ "image": "imagen",
+ "tool_filter": "fíltro",
+ "canel": "canel",
+ "invite_membership_action": "Acción afiliación de miembros",
+ "viewer": "visualizador",
+ "editor": "editor",
+ "admin": "administrador",
+ "logging_in": "iniciando sesión",
+ "password_confirmation": "Confirmación de Contraseña",
+ "confirm_again": "Te hemos enviado un correo electrónico explicando cómo puedes confirmar tu dirección de correo electrónico.",
+ "confirmed": "Tú Cuenta ha sido confirmada satisfactoriamente. Gracias.",
+ "signing_up": "Registrándote",
+ "password_check_inbox": "Por favor, comprueba tu bandeja de entrada de correo electrónico",
+ "new_space": "Nuevo Espacio",
+ "tool_more": "Más",
+ "what_is_your_name": "¡Bienvenido a %s! Por favor, elige un nombre de usuario.",
+ "lang": "es",
+ "landing_title": "Tu Pizarra en la Web.",
+ "landing_claim": "Spacedeck te permite combinar fácilmente todo tipo de multimedia en pizarras virtuales: notas de texto, fotos, enlaces web, incluso videos y grabaciones de audio. ",
+ "landing_example": "Las personas usan Spacedeck para organizar en equipo sus ideas y así poder ver proyectos completos de un vistazo, o bien en escuelas y universidades para obtener experiencias de aprendizaje más enriquecedoras y conectadas.",
+ "spaces": "Mis Espacios",
+ "access_editor_link": "Enlace de Edición Instantanea",
+ "access_editor_link_desc": "Proporciona este enlace a cualquier persona que deba poder editar instantáneamente este Espacio, no se requiere una cuenta: ",
+ "access_editor_link_desc_slug": "Este enlace también contiene el nombre del Espacio. ",
+ "access_anonymous_edit_blocking": "Los editores anónimos únicamente pueden cambiar sus propios elementos",
+ "access_current_members": "Miembros Actuales",
+ "access_new_members": "Invita Nuevos Miembros",
+ "access_no_members": "Los Miembros de este Espacio se mostrarán aquí.",
+ "comments": "comentarios",
+ "landing_customers": "Confiado por multitudes.",
+ "landing_features_title": "Sencillo de usar.",
+ "landing_features_text": "El nuevo Spacedeck 6 tiene un hermoso y optimizado interfaz de usuario que hace que tu trabajo sea más fácil y divertido que nunca, al tiempo que te brinda funciones aún más poderosas:",
+ "landing_features_1": "Arrastra & suelta imágenes, vídeos y áudios desde tu computadora o desde la web",
+ "landing_features_2": "Escribe texto y formatéalo con pleno control sobre fuente, color y estilo",
+ "landing_features_3": "Dibuja, anota y resalta incluyendo contornos gráficos",
+ "landing_features_4": "Convierte tu tablero en un área de presentación con zoom",
+ "landing_features_5": "Colabora y chatea en tiempo real con compañeros de equipo, alumnos y amigos.",
+ "landing_features_6": "Comparte Espacios en la web o via correo electrónico",
+ "landing_features_7": "Exporta tu trabajo como fichero imprimible PDF o como ZIP",
+ "landing_pricing": "Increiblemente asequible.",
+ "landing_pricing_lite": "Uso Libre/Personal",
+ "landing_pricing_lite_text": "La versión sencilla y completa para recopilar imágenes y tomar notas.",
+ "landing_pricing_pro_features_list": "
Espacios ilimitados
Estructura de Directorios
Exportación a ficheros PDF y ZIP
Sin Marcas de Agua
Personaliza tu fondo
Historial de Actividad
20 GB de Almacenamiento
",
+ "landing_pricing_pro": "€4,90/Usuario/Mes o 49,90/Usuario/Año",
+ "landing_pricing_pro_text": "Con todo la potencia que esperas.",
+ "landing_pricing_pro_features": "Con toda la potencia que esperas.",
+ "welcome_subject": "Bienvenido a Spacedeck",
+ "welcome_body": "¡Hola!\nGracias por registrárte en Spacedeck. Esperamos que disfrutes trabajando con Espacios. Recuerda, tu cuenta incluye colaboradores ilimitados. Siénte libre de compartir tus Espacios con amigos y colegas de todo el mundo.",
+ "invite_emails": "Dirección/ones de correo electrónico separadas por coma (,)",
+ "history_recently_updated": "Recientemente Actualizado",
+ "history_recently_empty": "Aún no ha pasado nada.",
+ "parent_folder": "Directorio padre",
+ "created_by": "Creado por",
+ "last_updated": "Última actualización",
+ "feedback_sent": "¡Muchas gracias por tu comentarios!",
+ "role_member": "Miembro",
+ "team_invite_membership_action": "Aceptar invitación",
+ "space_message_subject": "Nuevo Mensaje en el Espacio %s",
+ "space_message_body": "%s escribió en %s: \n",
+ "pro_ad_history_headline": "Cuando actualices a Spacedeck Pro, verás el historial de actualizaciones recientes en todos tus Espacios (compartidos) aquí.",
+ "password_reset_subject": "Restablecer contraseña para Spacedeck",
+ "password_reset_body": "Has solicitado el restablecimiento de tu contraseña en Spacedeck.\nPor favor, clica en el siguiente enlace para establecer una nueva contraseña.",
+ "password_reset_action": "Restablecer Ahora",
+ "was_offline": "La conexión con Spacedeck se ha interrupido. Si tienes trabajo sin salvar, mantén abierta, por favor, esta pestaña del navegadorhasta que la conexión esté restablecida, entonces toca los objetos no salvados.",
+ "subscription_failed_user_subject": "Problemas con el pago en tu Spacedeck",
+ "subscription_failed_user_body": "Desafortunadamente, no pudimos procesar su método de pago. Puede crear fácilmente un nuevo método de pago que incluya PayPal en la configuración de su cuenta.",
+ "subscription_failed_team_subject": "Problemas con el pago en tu Spacedeck",
+ "subscription_failed_team_body": "Desafortunadamente, no pudimos procesar su método de pago para tu Cuenta de Equipo. Corrija su método de pago lo antes posible.",
+ "team_name": "Nombre del Equipo",
+ "subdomain": "Subdominio",
+ "team_adresses": "Direcciones de correo electrónico del Equipo",
+ "add": "Añadir",
+ "invited": "invitado",
+ "duplicate_destination": "¿En qué directorio quieres duplicar este Espacio??",
+ "duplicate_confirm": "Duplicar %s en la directorio %s?",
+ "duplicate_success": "%s fue duplicado en %s.",
+ "goto_space": "Ve al Espacio %s",
+ "goto_folder": "Ve al Directorio %s",
+ "stay_here": "Permanece aquí",
+ "sharing": "Compartiendo",
+ "list": "Lista para Exportar",
+ "link": "Enlace",
+ "download_space": "Espacio de Descarga",
+ "type": "Tipo",
+ "download": "Descarga",
+ "Previous Zone": "Zona Previa",
+ "Next Zone": "Zona Siguiente",
+ "promote": "Promover",
+ "demote": "Degradar",
+ "more": "Más",
+ "lock": "Bloquear",
+ "unlock": "Desbloquear",
+ "follow_present": "Seguir",
+ "mute_present": "Dejar de Seguir",
+ "follow_present_help": "Si alguien más está presentando este espacio, los otros miembros siguen automáticamente la presentación. Active o desactive el seguimiento con este botón.",
+ "export": "exportar"
+}
diff --git a/locales/oc.js b/locales/oc.js
new file mode 100644
index 0000000000000000000000000000000000000000..a0f746036cbd85ca6ce40954f3b1aad43c3f0401
--- /dev/null
+++ b/locales/oc.js
@@ -0,0 +1,324 @@
+{
+ "ok": "D'acòrdi",
+ "cancel": "Anullar",
+ "close": "Tampar",
+ "open": "Dobrir",
+ "folder": "Repertòri",
+ "save": "Enregistrar",
+ "saved": "Enregistrat",
+ "created": "creat",
+ "duplicate": "Duplicar",
+ "delete": "Suprimir",
+ "remove": "Suprimir",
+ "set": "definir",
+ "reset": "reïnicializar",
+ "thanks": "Mercés",
+ "share": "Partejar",
+ "signup": "S’inscriure",
+ "login": "Connexion",
+ "logout": "Se desconnectar",
+ "email": "Adreça electronica",
+ "password": "Senhal",
+ "width": "Largor",
+ "height": "Nautor",
+ "nick": "Escais",
+ "role": "Ròtle",
+ "members": "Membres",
+ "actions": "Accions",
+ "or": "o",
+ "you": "vos",
+ "via": "via",
+ "by": "per",
+ "zero": "Zéro",
+ "page": "Pagina",
+ "new": "Nòu",
+ "copy": "Copiar",
+ "home": "Acuèlh",
+ "owner": "Proprietari",
+ "space": "Espaci",
+ "second": "Segond",
+ "not_found": "Pas trobat.",
+ "untitled_space": "Espaci sens nom",
+ "untitled_folder": "Repertòri sens nom",
+ "untitled": "sens títol",
+ "sure": "O volètz vertadièrament ?",
+ "specify": "Mercés d’especificar",
+ "confirm": "Mercés de confirmar",
+ "error_unknown_email": "Aquesta combinason d’adreça electronica/senhal es desconeguda.",
+ "error_password_confirmation": "Los senhals picats correspondon pas.",
+ "error_domain_blocked": "Lo domeni es blocat.",
+ "error_user_email_already_used": "Aquesta adreça es ja utilizada.",
+ "support": "Assisténcia Spacedeck",
+ "offline": "Fòra linha. Clicatz per mai d’opcions.",
+ "error": "O planhèm, quicòm a trucat. Mercés de contactar support@spacedeck.com",
+ "welcome": "La benvenguda",
+ "claim": "Vòstre tablèu numeric.",
+ "trynow": "Ensajatz ara.",
+ "about": "A prepaus de nosautre",
+ "terms": "Tèrmes",
+ "contact": "Contacte",
+ "privacy": "Confidencialitat",
+ "business_adress": "Adreça professionala",
+ "post_adress": "Adreça postala",
+ "phone": "Telefòn",
+ "ceo": "Gestionari",
+ "name": "Nom",
+ "confirm_subject": "Corrièl de confirmacion de Spacedeck",
+ "confirm_body": "Mercés de vòstra inscripcion a Spacedeck.\nMercés de clicar lo ligam seguent per confirmar vòstra adreça electronica.\n",
+ "confirm_action": "Confirmar",
+ "team_invite_membership_subject": "Invitacion d’equipa per %s",
+ "team_invite_membership_body": "Qualqu’un vos a convidat a %s sus Spacedeck. Mercés de clicar sul ligam seguent per acceptar l’invitacion.",
+ "team_invite_user_body": "Qualqu’un vos a convidat a %s sus Spacedeck.\nVòstre senhal temporari es « %s ».\nMercés de clicar sul ligam seguent per acceptar l’invitacion.",
+ "team_invite_admin_body": "%s es estat convidat a vòstra equipa : %s. Lo senhal temporari es « %s ».",
+ "team_invite_membership_acction": "Acceptar",
+ "team_new_member_subject": "Membre novèl",
+ "team_new_member_body": "%s a rejonch l’equipa %s sus Spacedeck",
+ "space_invite_membership_subject": "Invitacion Espaci per %s : %s",
+ "space_invite_membership_body": "%s vos a convit a l’Espaci « %s »",
+ "space_invite_membership_action": "Acceptar l’invitacion",
+ "folder_invite_membership_subject": "Espaci",
+ "folder_invite_membership_body": "Qualqu’un vos a convidat a Team sus Spacedeck. Clicatz lo ligam seguent per acceptar l’invitacion.",
+ "folder_invite_membership_acction": "Acceptar",
+ "login_google": "S’identificar amb Google",
+ "save_changes": "Enregistrar las modificacions",
+ "upgrade": "Metre a jorn",
+ "upgrade_now": "Metre a nivèl ara",
+ "create_space": "Crear un espaci",
+ "create_folder": "Crear un repertòri",
+ "email_unconfirmed": "Adreça pas confirmada",
+ "confirmation_sent": "Messatge enviat",
+ "folder_filter": "Filtre",
+ "sort_by": "Triar per",
+ "last_modified": "Darrièra modificacion",
+ "last_opened": "Darrièra dobertura",
+ "title": "Títol",
+ "edit_team": "Modificar equipa",
+ "edit_account": "Modificar compte",
+ "log_out": "Se desconnectar",
+ "no_spaces_yet": "Avètz pas encara creat cap d’espacis.",
+ "new_folder_title": "Novèl títol pel repertòri",
+ "folder_settings": "Paramètres repertòri",
+ "upload_cover_image": "Enviar imatge cobèrta",
+ "spacedeck_pro_ad_folders": "Avec Spacedeck Pro, vous pouvez organiser un nombre illimité de espaces dans les dossiers et gérer les contrôles d'accès pour chaque dossier. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_versions": "Avec Spacedeck Pro, vous pouvez enregistrer des versions illimitées de chaque espace pour suivre vos progrès ou de conserver des instantanés sécurité. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_pdf": "Avec Spacedeck Pro, vous pouvez exporter vos espaces et même des dossiers entiers belles PDF pour l'archivage, de diffusion, ou autour de l'impression. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_zip": "Avec Spacedeck Pro, vous pouvez exporter le contenu d'un espace comme un paquet ZIP. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_colors": "Avec Spacedeck Pro, vous pouvez mélanger vos propres couleurs en utilisant un sélecteur de couleur professionnelle.",
+ "profile_caption": "Perfil",
+ "upload_avatar": "Enviar avatar",
+ "uploading_avatar": "Mandadís avatar…",
+ "avatar_dimensions": "Dimensions recomandadas : 200x200 pixèls.",
+ "profile_name": "Nom",
+ "profile_email": "Adreça electronica",
+ "send_again": "Tornar enviar",
+ "confirmation_sent_long": "Ligam de confirmacion enviat. Mercés de verificar vòstres corrièrs.",
+ "confirmation_sent_another": "Un autre ligam de confirmacion enviat.",
+ "confirmation_sent_dialog_text": "Avèm enviat un corrièl qu’explica cossí confirmar vòstra adreça electronica.",
+ "payment_caption": "Pagament",
+ "language_caption": "Lenga",
+ "notifications_caption": "Notificacions",
+ "notifications_option_chat": "Enviatz-me de comentaris novèls per corrièl",
+ "notifications_option_spaces": "Enviatz-me un resumit jornadièr de las modificacions dels espacis",
+ "password_caption": "Senhal",
+ "current_password": "Senhal actual",
+ "new_password": "Senhal novèl",
+ "verify_password": "Verificar lo senhal novèl",
+ "change_password": "Modificar senhal",
+ "reset_password": "Reïnicializar senhal",
+ "terminate_caption": "Suprimir lo compte",
+ "terminate_warning": "En escafant vòstre compte, vòstres messatges, espacis, repertòris e lor contengut seràn suprimits. Aquesta accion pòt pas èsser anullada.",
+ "terminate_warning2": "Aquò pòt pas èsser anullat.",
+ "terminate_reason": "Messatge",
+ "terminate_reason_caption": "Ajudatz-nos a melhorar lo logicial en nos diguent las rasons de la supression de vòstre compte",
+ "terminate_terminate": "Suprimir vòstre compte per totjorn ?",
+ "space_blank1": "Aquò es vòstre novèl espaci",
+ "space_blank2": "Lisatz de fichièrs, pegatz de ligams",
+ "space_blank3": "o utilizatz las aisinas",
+ "space_blank4": "Siatz creatius !",
+ "draft": "Borrolhon",
+ "publish": "Publicar",
+ "published": "Publicat",
+ "save_version": "Enregistrar version",
+ "version_saved": "Version enregistrada",
+ "post": "Publicar messatge",
+ "chat_invite_cta1": "Collaboratz amb amusament !",
+ "chat_invite_cta2": "Perqué pas ",
+ "chat_invite_cta3": "convidar de monde",
+ "chat_invite_cta4": "per trabalhar amb vos ?",
+ "chat_message_placeholder": "Escrivètz vòstre messatge…",
+ "view": "Afichatge",
+ "edit": "Edicion",
+ "present": "Present",
+ "chat": "Messatjariá",
+ "meta": "Mèta",
+ "tool_search": "Recercar",
+ "tool_upload": "Enviar",
+ "tool_text": "Tèxte",
+ "tool_shape": "Forma",
+ "tool_zones": "Zònas",
+ "tool_canvas": "Canvas",
+ "search_media": "Cercar de mèdias…",
+ "type_here": "Picatz aquí",
+ "text_formats": "Formats",
+ "format_p": "Paragraph",
+ "format_bullets": "Lista a piuses",
+ "format_numbers": "Lista numeratada",
+ "format_h1": "Títol 1",
+ "format_h2": "Títol 2",
+ "format_h3": "Títol 3",
+ "font_size": "Font Size",
+ "line_height": "Nnautor de linha",
+ "tool_align": "Alinhar",
+ "tool_styles": "Estils",
+ "tool_bullets": "Bullets",
+ "tool_numbers": "Nombres",
+ "color_fill": "Fill",
+ "color_stroke": "Traçat",
+ "color_text": "Tèxte",
+ "tool_type": "Tipe",
+ "tool_box": "Bóstia",
+ "tool_link": "Ligam",
+ "tool_layout": "Agençament",
+ "tool_options": "Opcions",
+ "tool_stroke": "Traçat",
+ "tool_delete": "Suprimir",
+ "tool_lock": "Verrolhar",
+ "tool_copy": "Copiar",
+ "stack": "Pila",
+ "tool_circle": "Cercle",
+ "tool_hexagon": "Exagòn",
+ "tool_square": "Carrat",
+ "tool_diamond": "Diamond",
+ "tool_bubble": "Bulla",
+ "tool_cloud": "Nívol",
+ "tool_burst": "Burst",
+ "tool_star": "Star",
+ "tool_heart": "Còr",
+ "tool_scribble": "Barbolhatge",
+ "tool_line": "Linha",
+ "tool_arrow": "Sageta",
+ "search_media_placeholder": "Cercar de mèdias web…",
+ "add_zone": "Zòna novèla",
+ "palette": "Paleta",
+ "picker": "Pipeta",
+ "background_image_caption": "Imatge",
+ "background_color_caption": "Color",
+ "upload_background_caption": "Clicar per enviar un imatge de rèireplan",
+ "upload_background": "Enviar rèireplan",
+ "access_caption": "Accès",
+ "versions_caption": "Versions",
+ "info_caption": "Info",
+ "mode_private": "Privat : sonque los membres pòdon veire o modificar",
+ "mode_public": "Public : qual que siá amb lo ligam pòt veire",
+ "invite_collaborators": "Convidar collaborators",
+ "revoke_access": "Revocar l’accès",
+ "invite": "Enviar invitacions",
+ "invitee_email_address": "Adreça electronica del novèl membre",
+ "optional_message": "Messatge opcional",
+ "role_viewer": "Visualizaira",
+ "role_editor": "Editor",
+ "role_admin": "Admin",
+ "new_space_title": "Títol novèl per l’Espaci",
+ "team": "Equipa",
+ "search": "Recercar",
+ "search_no_results": "search_no_results",
+ "search_clear": "search_clear",
+ "rename": "Renomenar",
+ "mobile": "mobil",
+ "image": "imatge",
+ "tool_filter": "filtre",
+ "canel": "canel",
+ "invite_membership_action": "invite_membership_action",
+ "viewer": "visualizaira",
+ "editor": "editor",
+ "admin": "admin",
+ "logging_in": "connexion",
+ "password_confirmation": "Confirmacion del senhla",
+ "confirm_again": "Mercés de consultar vòstra bóstia de recepcion per confirmar vòstra adreça.",
+ "confirmed": "Vòstre compte es estat corrèctament confirmat. Mercés.",
+ "signing_up": "Inscripcion",
+ "password_check_inbox": "Verificatz vòstra bóstia de recepcion",
+ "new_space": "Espaci novèl",
+ "tool_more": "Mai",
+ "what_is_your_name": "La benvenguda a %s ! Mercés de causir un escais-nom.",
+ "lang": "en",
+ "landing_title": "Vòstre tablèu blanc sul Web.",
+ "landing_claim": "Spacedeck vos permet de facilament combinar quin que siá tipe de mèdias sus un tablèu virtual : tèxte, nòtas, ligams web, amai vidèos e enregistraments àudio. ",
+ "landing_example": "Lo monde utiliza Spacedeck per organizar lors idèas, en equipa per veire totes los projèctes en una ulhada, a l’escòla e a l’universitat pels mai rics, experiéncia d’aprendissatge connectat.",
+ "spaces": "Mos espacis",
+ "access_editor_link": "Ligam de modificacion dirècta",
+ "access_editor_link_desc": "Donatz aqueste ligam a qualqu’un que deu poder modificar dirèctament aqueste Espaci, cap de compte pas requerit : ",
+ "access_editor_link_desc_slug": "Aqueste ligam conten lo nom de l’espaci, tanben. ",
+ "access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements qu’an creats.",
+ "access_current_members": "Membres actuals",
+ "access_new_members": "Convidar de novèls membres",
+ "access_no_members": "Los membres d’aqueste Espacii apreissaràn aquí.",
+ "comments": "comentaris",
+ "landing_customers": "La fisança de milièr de personas.",
+ "landing_features_title": "Un jòc d'enfants d’utilizar.",
+ "landing_features_text": "Le tout nouveau Spacedeck 5 vous permet de travailler bien plus facilement grâce à sa magnifique interface simplifiée.",
+ "landing_features_1": "Glissez & déposez images, vidéos et audios de votre ordinateur ou du web",
+ "landing_features_2": "Ecrivez directement sur l'espace et choisissez les polices de caractère, couleurs et styles",
+ "landing_features_3": "Dessinez, annotez et surlignez grâce aux formes graphiques intégrées",
+ "landing_features_4": "Transformez votre espace en une présentation dynamique",
+ "landing_features_5": "Collaborez et discutez en temps réel avec vos collègues, élèves et amis",
+ "landing_features_6": "Partagez vos espaces sur le web ou par email",
+ "landing_features_7": "Exportez votre espace en PDF pour l'imprimer",
+ "landing_pricing": "Incroyablement abordable.",
+ "landing_pricing_lite": "Usage personnel",
+ "landing_pricing_lite_text": "La version de base, bien arrondi pour recueillir des images et de garder des notes.",
+ "landing_pricing_pro_features_list": "
Unlimited Spaces
Exporter PDF, ZIP
No Watermarks
Image de fonds
Activity History
20 Go de stockage
",
+ "landing_pricing_pro": "€4,90/User/Mo. €49,90/User/Year",
+ "landing_pricing_pro_text": "Avec toute la puissance que vous attendez.",
+ "landing_pricing_pro_features": "€4,90/User/Mo. €49,90/User/Year",
+ "welcome_subject": "La benvenguda a Spacedeck",
+ "welcome_body": "Mercés per vòstra inscripcion a Spacedeck.\nEsperam qu’auretz plaser a trabalhar dins los Espacis. Oblidetz pas que vòstre compte conten un nombre illimitat de collaborators. Esitetz pas a partejar vòstres espacis amb los amics e collègas del monde entièr.",
+ "invite_emails": "Picatz las adreças mails (separadas per de vergulas)",
+ "history_recently_updated": "Novèlas",
+ "history_recently_empty": "Pas res",
+ "parent_folder": "Repertòri parent",
+ "created_by": "Creat per",
+ "last_updated": "Darrièra mesa a jorn",
+ "feedback_sent": "Comentari enviat",
+ "role_member": "Membre",
+ "team_invite_membership_action": "Acceptar",
+ "space_message_subject": "A publicat sus %s",
+ "space_message_body": "%s a comentat dins %s :\n",
+ "pro_ad_history_headline": "Aprèp una mesa a nivèl podètz obténer un apercebut de totas las activitats actualas dels espacis aquí.",
+ "password_reset_subject": "Reïnicializar lo senhal per Spacedeck",
+ "password_reset_body": "Òu !
Avètz demandat la reïnicializacion del senhal. Mercés de clicar sul ligam seguent per ne causir un novèl. ",
+ "password_reset_action": "Reïnicializar ara",
+ "was_offline": "La connexion a Spacedeck es estada copada. S’avètz de trabalh pas enregistratz, gardatz aqueste onglet de navigador dobèrt fins que la connexion siá restablida puèi tocatz de nòu los elements pas enregistrats.",
+ "subscription_failed_user_subject": "Problèma amb lo pagament Spacedeck",
+ "subscription_failed_user_body": "Unfortunately, we could not process your Payment-method. You can easly create a new payment method including PayPal in your account settings.",
+ "subscription_failed_team_subject": "Problem with your Spacedeck Payment",
+ "subscription_failed_team_body": "Unfortunately, we could not process your Payment-method for your Team-Account. Please fix your payment method asap.",
+ "team_name": "Nom de l’equipa",
+ "subdomain": "jos-domeni",
+ "team_adresses": "Adreças equipa",
+ "add": "Ajustar",
+ "invited": "convidat",
+ "duplicate_destination": "Seleccionatz lo repertòri de destinacion",
+ "duplicate_confirm": "Duplicar %s dins %s ?",
+ "duplicate_success": "%s es estat duplicat dins %s.",
+ "goto_space": "anar a l’espaci",
+ "goto_folder": "anar al repertòri",
+ "stay_here": "Demorar aquí",
+ "sharing": "partatge",
+ "list": "lista",
+ "link": "Ligam",
+ "download_space": "Telecargar espaci",
+ "type": "Tipe",
+ "download": "Telecargar",
+ "Previous Zone": "Zòna precedenta",
+ "Next Zone": "Zòna seguenta",
+ "promote": "Promòure",
+ "demote": "Retrogradar",
+ "more": "Mai",
+ "lock": "Verrolhar",
+ "unlock": "Desverrolhar",
+ "follow_present": "Seguir",
+ "mute_present": "Quitar de seguir",
+ "follow_present_help": "follow_present_help",
+ "export": "exportar"
+}
diff --git a/middlewares/api_helpers.js b/middlewares/api_helpers.js
index 893f35f9e5da602c19c52d1f78ef973fa172b35d..551c7c885a5cd7c13eb348dddf16c32655198407 100644
--- a/middlewares/api_helpers.js
+++ b/middlewares/api_helpers.js
@@ -4,27 +4,6 @@ require('../models/db');
var config = require('config');
const redis = require('../helpers/redis');
-// FIXME TODO object.toJSON()
-
-var saveAction = (actionKey, object) => {
- if (object.constructor.modelName == "Space")
- return;
-
- let attr = {
- action: actionKey,
- space: object.space_id || object.space,
- user: object.user_id || object.user,
- editor_name: object.editor_name,
- object: object
- };
-
- /*let action = new Action(attr);
- action.save(function(err) {
- if (err)
- console.error("saved create action err:", err);
- });*/
-};
-
module.exports = (req, res, next) => {
res.header("Cache-Control", "no-cache");
@@ -36,21 +15,18 @@ module.exports = (req, res, next) => {
if (!object) return;
redis.sendMessage("create", model, object, req.channelId);
this.status(201).json(object);
- saveAction("create", object);
};
res['distributeUpdate'] = function(model, object) {
if (!object) return;
redis.sendMessage("update", model, object, req.channelId);
this.status(200).json(object);
- saveAction("update", object);
};
res['distributeDelete'] = function(model, object) {
if (!object) return;
redis.sendMessage("delete", model, object, req.channelId);
this.sendStatus(204);
- saveAction("delete", object);
};
next();
diff --git a/middlewares/session.js b/middlewares/session.js
index cc5be986d3425afba165ecac1b11b10ff36af435..b9e8a0bfa4493dee03811c11d0518f981f0474f9 100644
--- a/middlewares/session.js
+++ b/middlewares/session.js
@@ -4,8 +4,27 @@ const db = require('../models/db');
var config = require('config');
module.exports = (req, res, next) => {
+
+ // authentication via API token
+ const api_token = req.headers["x-spacedeck-api-token"];
+
+ if (api_token && api_token.length>7) {
+ db.User.findOne({where: {api_token: api_token}}).then(user => {
+ req.user = user;
+ next();
+ }).error(err => {
+ res.status(403).json({
+ "error": "invalid_api-token"
+ });
+ next();
+ });
+
+ return;
+ }
+
+ // authentication via session/cookie
const token = req.cookies["sdsession"];
-
+
if (token && token != "null" && token != null) {
db.Session.findOne({where: {token: token}})
.then(session => {
@@ -28,7 +47,7 @@ module.exports = (req, res, next) => {
} else {
res.send("Please clear your cookies and try again.");
}
-
+
} else {
req["token"] = token;
req["user"] = user;
@@ -44,4 +63,3 @@ module.exports = (req, res, next) => {
next();
}
}
-
diff --git a/middlewares/space_helpers.js b/middlewares/space_helpers.js
index 07e5931d7893cafe550c8c17ad8d443bb63ae7a3..5f137cb0030fe329d4066fdaf431f0b55f2e216d 100644
--- a/middlewares/space_helpers.js
+++ b/middlewares/space_helpers.js
@@ -1,6 +1,7 @@
'use strict';
const db = require('../models/db');
+const { Op } = require("sequelize");
var config = require('config');
module.exports = (req, res, next) => {
@@ -53,15 +54,14 @@ module.exports = (req, res, next) => {
'email': 1
};
+ // find space by id or slug
db.Space.findOne({where: {
- "_id": spaceId
+ [Op.or]: [
+ {"_id": spaceId},
+ {"edit_slug": spaceId}
+ ]
}}).then(function(space) {
- //.populate("creator", userMapping)
- //if (err) {
- // res.status(400).json(err);
- //} else {
-
if (space) {
if (space.access_mode == "public") {
if (space.password) {
diff --git a/models/db.js b/models/db.js
index cda2c32064dc548ef4e0379f65e07afb1d4c5eb2..1024b543e5f563bee06e6b8c69c54b5952a609fc 100644
--- a/models/db.js
+++ b/models/db.js
@@ -1,4 +1,5 @@
const Umzug = require('umzug');
+const config = require('config')
function sequel_log(a,b,c) {
console.log(a);
@@ -17,7 +18,7 @@ const sequelize = new Sequelize('database', 'username', 'password', {
},
// SQLite only
- storage: 'database.sqlite',
+ storage: config.get('storage_local_db'),
logging: sequel_log,
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
@@ -42,6 +43,7 @@ module.exports = {
avatar_thumb_uri: Sequelize.STRING,
confirmation_token: Sequelize.STRING,
password_reset_token: Sequelize.STRING,
+ api_token: Sequelize.STRING,
home_folder_id: Sequelize.STRING,
prefs_language: Sequelize.STRING,
prefs_email_notifications: Sequelize.STRING,
@@ -50,6 +52,17 @@ module.exports = {
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
+ CreatorSafeInclude: function(db) {
+ return {
+ model: this.User,
+ as: 'creator',
+ attributes: ['_id','email','nickname',
+ 'avatar_original_uri',
+ 'avatar_thumb_uri',
+ 'created_at','updated_at']
+ };
+ },
+
Session: sequelize.define('session', {
token: {type: Sequelize.STRING, primaryKey: true},
user_id: Sequelize.STRING,
@@ -91,7 +104,8 @@ module.exports = {
user_id: Sequelize.STRING,
role: Sequelize.STRING,
code: Sequelize.STRING,
- state: {type: Sequelize.STRING, defaultValue: "pending"},
+ state: {type: Sequelize.STRING, defaultValue: "pending"}, // valid: "pending", "active"
+ email_invited: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
@@ -278,21 +292,20 @@ module.exports = {
getUserRoleInSpace: (originalSpace, user, cb) => {
originalSpace.path = [];
- console.log("getUserRoleInSpace",originalSpace._id,user._id,user.home_folder_id);
-
+
if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) {
cb("admin");
} else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.findAll({ where: {
- "space": space._id
+ "space_id": space._id
}}).then(function(parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) {
Space.findOne({ where: {
"_id": space.parent_space_id
- }}, function(err, parentSpace) {
+ }}).then(function(parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
});
} else {
diff --git a/models/migrations/01-spaces-delete-cascade.js b/models/migrations/01-spaces-delete-cascade.js
index a1c2f4624edbfff666445a88d7d71f32da1e31db..a21e00545c89cf6eac0d9615940f87d6fc5fac71 100644
--- a/models/migrations/01-spaces-delete-cascade.js
+++ b/models/migrations/01-spaces-delete-cascade.js
@@ -2,7 +2,7 @@
module.exports = {
up: function(migration, DataTypes) {
- return [
+ return Promise.all([
migration.changeColumn('memberships', 'space_id',
{
type: DataTypes.STRING,
@@ -36,11 +36,11 @@ module.exports = {
onUpdate: 'CASCADE'
}
)
- ]
+ ])
},
down: function(migration, DataTypes) {
- return [
+ return Promise.all([
migration.changeColumn('memberships', 'space_id',
{
type: DataTypes.STRING,
@@ -52,7 +52,6 @@ module.exports = {
onUpdate: 'NO ACTION'
}
),
- ,
migration.changeColumn('artifacts', 'space_id',
{
type: DataTypes.STRING,
@@ -75,6 +74,6 @@ module.exports = {
onUpdate: 'NO ACTION'
}
)
- ]
+ ])
}
-};
+}
diff --git a/models/migrations/02-users-add-api-token.js b/models/migrations/02-users-add-api-token.js
new file mode 100644
index 0000000000000000000000000000000000000000..4726c9d7bdcfaaa0689893fd3c145865a7b04376
--- /dev/null
+++ b/models/migrations/02-users-add-api-token.js
@@ -0,0 +1,23 @@
+'use strict';
+
+module.exports = {
+ up: function(migration, DataTypes) {
+ return Promise.all([
+ migration.changeColumn('users', 'api_token',
+ {
+ type: DataTypes.STRING
+ }
+ )
+ ])
+ },
+
+ down: function(migration, DataTypes) {
+ return Promise.all([
+ migration.changeColumn('users', 'api_token',
+ {
+ type: Sequelize.STRING
+ }
+ )
+ ])
+ }
+}
diff --git a/package.json b/package.json
index 34bbd9fa4e0ece74f9a394c4674eed429e3308ef..d3991ee6d1c6ed41250d55b4fd6727be7da2978a 100644
--- a/package.json
+++ b/package.json
@@ -3,49 +3,54 @@
"version": "1.0.0",
"private": true,
"scripts": {
- "start": "electron ."
+ "start": "node spacedeck.js"
},
"engines": {
- "node": ">=7.8.0"
+ "node": ">=10.0.0"
},
"dependencies": {
"archiver": "1.3.0",
"async": "2.3.0",
- "basic-auth": "1.1.0",
"bcryptjs": "2.4.3",
- "body-parser": "~1.17.1",
+ "body-parser": "^1.19.0",
"cheerio": "0.22.0",
"config": "1.25.1",
"cookie-parser": "~1.4.3",
- "electron": "^1.8.4",
+ "ejs": "3.1.5",
"execSync": "latest",
- "express": "~4.13.0",
+ "express": "^4.16.4",
"file-type": "^7.6.0",
"glob": "7.1.1",
- "gm": "1.23.0",
+ "gm": "^1.23.1",
+ "gulp": "^4.0.2",
+ "gulp-concat": "^2.6.1",
+ "gulp-sass": "^4.0.2",
"helmet": "^3.5.0",
"i18n-2": "0.6.3",
"log-timestamp": "latest",
"mock-aws-s3": "^2.6.0",
"moment": "^2.19.3",
- "morgan": "1.8.1",
+ "morgan": "^1.9.1",
"node-phantom-simple": "2.2.4",
+ "node-server-screenshot": "^0.2.1",
"nodemailer": "^4.6.7",
- "phantomjs-prebuilt": "2.1.14",
+ "passport": "^0.4.1",
+ "passport-saml": "^1.3.5",
+ "phantomjs-prebuilt": "^2.1.16",
"read-chunk": "^2.1.0",
- "request": "2.81.0",
+ "request": "^2.88.0",
+ "saml2js": "^0.1.2",
"sanitize-html": "^1.11.1",
"sequelize": "^4.37.6",
"serve-favicon": "~2.4.2",
"serve-static": "^1.13.1",
- "slug": "0.9.1",
+ "slug": "^1.1.0",
"sqlite3": "^4.0.0",
- "swig": "1.4.2",
"umzug": "^2.1.0",
"underscore": "1.8.3",
"uuid": "^3.2.1",
"validator": "7.0.0",
- "ws": "2.2.3"
+ "ws": "3.3.1"
},
"main": "app.js",
"description": "",
diff --git a/public/images/favicon.png b/public/images/favicon.png
index 5b28e0cf907646e7dfdf8d0e75126b154a76a873..ee15d207465b75a2f46c55210fcb60c20297e053 100644
Binary files a/public/images/favicon.png and b/public/images/favicon.png differ
diff --git a/public/images/sd6-icon-white.svg b/public/images/sd6-icon-white.svg
new file mode 100644
index 0000000000000000000000000000000000000000..58b2774b7efd9c7c393f6a380c2617e12a25bd59
--- /dev/null
+++ b/public/images/sd6-icon-white.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/public/images/sd6-icon.svg b/public/images/sd6-icon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e645c61f72855ae8b832aacf00f9bcb7b51cebf6
--- /dev/null
+++ b/public/images/sd6-icon.svg
@@ -0,0 +1,66 @@
+
+
+
+
diff --git a/public/images/sd6-logo-black.svg b/public/images/sd6-logo-black.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8e1360eadaf426dcd0dbd74bdadac2e3399d4b66
--- /dev/null
+++ b/public/images/sd6-logo-black.svg
@@ -0,0 +1,129 @@
+
+
+
+
diff --git a/public/images/sd6-screenshot.png b/public/images/sd6-screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a6fdaca46321bfc5ab3662c154a0abb717fa239
Binary files /dev/null and b/public/images/sd6-screenshot.png differ
diff --git a/public/javascripts/backend.js b/public/javascripts/backend.js
index 5739feac35ce0ec6c0f38ef79bcc7de347273808..2eb76c3c11a4ad6129ed75641821817d7829e8e6 100644
--- a/public/javascripts/backend.js
+++ b/public/javascripts/backend.js
@@ -6,6 +6,10 @@ var websocket = null;
var channel_id = null;
var space_auth = null;
+function set_space_auth(hash) {
+ space_auth = hash;
+}
+
function load_resource(method, path, data, on_success, on_error, on_progress) {
var req = new XMLHttpRequest();
req.onload = function(evt,b,c) {
@@ -44,24 +48,17 @@ function load_resource(method, path, data, on_success, on_error, on_progress) {
}
req.withCredentials = true;
-
req.open(method, api_endpoint+"/api"+path, true);
if (api_token) {
req.setRequestHeader("X-Spacedeck-Auth", api_token);
}
-
if (space_auth) {
- console.log("set space auth", space_auth);
req.setRequestHeader("X-Spacedeck-Space-Auth", space_auth);
}
-
if (channel_id) {
req.setRequestHeader("X-Spacedeck-Channel", channel_id);
}
- if (csrf_token) {
- req.setRequestHeader("X-csrf-token", csrf_token);
- }
try {
if (data) {
@@ -133,18 +130,6 @@ function load_spaces(id, is_home, on_success, on_error) {
}, on_error);
}
-function load_importables(user, on_success, on_error) {
- load_resource("get", "/users/"+user._id+"/importables", null, on_success, on_error);
-}
-
-function import_zip(user, filename, on_success, on_error) {
- load_resource("get", "/users/"+user._id+"/import?zip="+filename, null, on_success, on_error);
-}
-
-function load_writable_folders(on_success, on_error) {
- load_resource("get", "/spaces?writablefolders=true", null, on_success, on_error);
-}
-
function load_history(s, on_success, on_error) {
load_resource("get", "/spaces/"+ s._id +"/digest", null, on_success, on_error);
}
@@ -190,12 +175,10 @@ function delete_space(s, on_success, on_error) {
load_resource("delete", "/spaces/"+s._id, null, on_success, on_error);
}
-
function delete_artifact(a, on_success, on_error) {
load_resource("delete", "/spaces/"+a.space_id+"/artifacts/"+a._id);
}
-
function duplicate_space(s, to_space_id, on_success, on_error) {
var path = "/spaces/"+s._id+"/duplicate";
if(to_space_id) {
@@ -274,8 +257,8 @@ function delete_user(u, password, on_success, on_error) {
load_resource("delete", "/users/"+u._id +"?password="+password,null,on_success,on_error);
}
-function create_user(name, email, password, password_confirmation, on_success, on_error) {
- load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation}, on_success, on_error);
+function create_user(name, email, password, password_confirmation, invite_code, on_success, on_error) {
+ load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation, invite_code: invite_code}, on_success, on_error);
}
function create_session(email, password, on_success, on_error) {
diff --git a/public/javascripts/locales.js b/public/javascripts/locales.js
index 086769fa85c134d428040f5a533d29e6b4abd664..8faf9093a3be447dcac200e8c9e4b256cf05378c 100644
--- a/public/javascripts/locales.js
+++ b/public/javascripts/locales.js
@@ -2,6 +2,7 @@ window.locales = {};
window.locales.en = {};
window.locales.de = {};
window.locales.fr = {};
+window.locales.oc = {};
window.locales.en.translation =
{
"ok": "OK",
@@ -943,4 +944,328 @@ window.locales.fr.translation =
"promote": "promouvoir",
"demote": "rétrograder"
}
-
+window.locales.oc.translation =
+{
+ "ok": "D'acòrdi",
+ "cancel": "Anullar",
+ "close": "Tampar",
+ "open": "Dobrir",
+ "folder": "Repertòri",
+ "save": "Enregistrar",
+ "saved": "Enregistrat",
+ "created": "creat",
+ "duplicate": "Duplicar",
+ "delete": "Suprimir",
+ "remove": "Suprimir",
+ "set": "definir",
+ "reset": "reïnicializar",
+ "thanks": "Mercés",
+ "share": "Partejar",
+ "signup": "S’inscriure",
+ "login": "Connexion",
+ "logout": "Se desconnectar",
+ "email": "Adreça electronica",
+ "password": "Senhal",
+ "width": "Largor",
+ "height": "Nautor",
+ "nick": "Escais",
+ "role": "Ròtle",
+ "members": "Membres",
+ "actions": "Accions",
+ "or": "o",
+ "you": "vos",
+ "via": "via",
+ "by": "per",
+ "zero": "Zéro",
+ "page": "Pagina",
+ "new": "Nòu",
+ "copy": "Copiar",
+ "home": "Acuèlh",
+ "owner": "Proprietari",
+ "space": "Espaci",
+ "second": "Segond",
+ "not_found": "Pas trobat.",
+ "untitled_space": "Espaci sens nom",
+ "untitled_folder": "Repertòri sens nom",
+ "untitled": "sens títol",
+ "sure": "O volètz vertadièrament ?",
+ "specify": "Mercés d’especificar",
+ "confirm": "Mercés de confirmar",
+ "error_unknown_email": "Aquesta combinason d’adreça electronica/senhal es desconeguda.",
+ "error_password_confirmation": "Los senhals picats correspondon pas.",
+ "error_domain_blocked": "Lo domeni es blocat.",
+ "error_user_email_already_used": "Aquesta adreça es ja utilizada.",
+ "support": "Assisténcia Spacedeck",
+ "offline": "Fòra linha. Clicatz per mai d’opcions.",
+ "error": "O planhèm, quicòm a trucat. Mercés de contactar support@spacedeck.com",
+ "welcome": "La benvenguda",
+ "claim": "Vòstre tablèu numeric.",
+ "trynow": "Ensajatz ara.",
+ "about": "A prepaus de nosautre",
+ "terms": "Tèrmes",
+ "contact": "Contacte",
+ "privacy": "Confidencialitat",
+ "business_adress": "Adreça professionala",
+ "post_adress": "Adreça postala",
+ "phone": "Telefòn",
+ "ceo": "Gestionari",
+ "name": "Nom",
+ "confirm_subject": "Corrièl de confirmacion de Spacedeck",
+ "confirm_body": "Mercés de vòstra inscripcion a Spacedeck.\nMercés de clicar lo ligam seguent per confirmar vòstra adreça electronica.\n",
+ "confirm_action": "Confirmar",
+ "team_invite_membership_subject": "Invitacion d’equipa per %s",
+ "team_invite_membership_body": "Qualqu’un vos a convidat a %s sus Spacedeck. Mercés de clicar sul ligam seguent per acceptar l’invitacion.",
+ "team_invite_user_body": "Qualqu’un vos a convidat a %s sus Spacedeck.\nVòstre senhal temporari es « %s ».\nMercés de clicar sul ligam seguent per acceptar l’invitacion.",
+ "team_invite_admin_body": "%s es estat convidat a vòstra equipa : %s. Lo senhal temporari es « %s ».",
+ "team_invite_membership_acction": "Acceptar",
+ "team_new_member_subject": "Membre novèl",
+ "team_new_member_body": "%s a rejonch l’equipa %s sus Spacedeck",
+ "space_invite_membership_subject": "Invitacion Espaci per %s : %s",
+ "space_invite_membership_body": "%s vos a convit a l’Espaci « %s »",
+ "space_invite_membership_action": "Acceptar l’invitacion",
+ "folder_invite_membership_subject": "Espaci",
+ "folder_invite_membership_body": "Qualqu’un vos a convidat a Team sus Spacedeck. Clicatz lo ligam seguent per acceptar l’invitacion.",
+ "folder_invite_membership_acction": "Acceptar",
+ "login_google": "S’identificar amb Google",
+ "save_changes": "Enregistrar las modificacions",
+ "upgrade": "Metre a jorn",
+ "upgrade_now": "Metre a nivèl ara",
+ "create_space": "Crear un espaci",
+ "create_folder": "Crear un repertòri",
+ "email_unconfirmed": "Adreça pas confirmada",
+ "confirmation_sent": "Messatge enviat",
+ "folder_filter": "Filtre",
+ "sort_by": "Triar per",
+ "last_modified": "Darrièra modificacion",
+ "last_opened": "Darrièra dobertura",
+ "title": "Títol",
+ "edit_team": "Modificar equipa",
+ "edit_account": "Modificar compte",
+ "log_out": "Se desconnectar",
+ "no_spaces_yet": "Avètz pas encara creat cap d’espacis.",
+ "new_folder_title": "Novèl títol pel repertòri",
+ "folder_settings": "Paramètres repertòri",
+ "upload_cover_image": "Enviar imatge cobèrta",
+ "spacedeck_pro_ad_folders": "Avec Spacedeck Pro, vous pouvez organiser un nombre illimité de espaces dans les dossiers et gérer les contrôles d'accès pour chaque dossier. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_versions": "Avec Spacedeck Pro, vous pouvez enregistrer des versions illimitées de chaque espace pour suivre vos progrès ou de conserver des instantanés sécurité. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_pdf": "Avec Spacedeck Pro, vous pouvez exporter vos espaces et même des dossiers entiers belles PDF pour l'archivage, de diffusion, ou autour de l'impression. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_zip": "Avec Spacedeck Pro, vous pouvez exporter le contenu d'un espace comme un paquet ZIP. Voulez-vous en savoir plus sur les fonctionnalités Pro ?",
+ "spacedeck_pro_ad_colors": "Avec Spacedeck Pro, vous pouvez mélanger vos propres couleurs en utilisant un sélecteur de couleur professionnelle.",
+ "profile_caption": "Perfil",
+ "upload_avatar": "Enviar avatar",
+ "uploading_avatar": "Mandadís avatar…",
+ "avatar_dimensions": "Dimensions recomandadas : 200x200 pixèls.",
+ "profile_name": "Nom",
+ "profile_email": "Adreça electronica",
+ "send_again": "Tornar enviar",
+ "confirmation_sent_long": "Ligam de confirmacion enviat. Mercés de verificar vòstres corrièrs.",
+ "confirmation_sent_another": "Un autre ligam de confirmacion enviat.",
+ "confirmation_sent_dialog_text": "Avèm enviat un corrièl qu’explica cossí confirmar vòstra adreça electronica.",
+ "payment_caption": "Pagament",
+ "language_caption": "Lenga",
+ "notifications_caption": "Notificacions",
+ "notifications_option_chat": "Enviatz-me de comentaris novèls per corrièl",
+ "notifications_option_spaces": "Enviatz-me un resumit jornadièr de las modificacions dels espacis",
+ "password_caption": "Senhal",
+ "current_password": "Senhal actual",
+ "new_password": "Senhal novèl",
+ "verify_password": "Verificar lo senhal novèl",
+ "change_password": "Modificar senhal",
+ "reset_password": "Reïnicializar senhal",
+ "terminate_caption": "Suprimir lo compte",
+ "terminate_warning": "En escafant vòstre compte, vòstres messatges, espacis, repertòris e lor contengut seràn suprimits. Aquesta accion pòt pas èsser anullada.",
+ "terminate_warning2": "Aquò pòt pas èsser anullat.",
+ "terminate_reason": "Messatge",
+ "terminate_reason_caption": "Ajudatz-nos a melhorar lo logicial en nos diguent las rasons de la supression de vòstre compte",
+ "terminate_terminate": "Suprimir vòstre compte per totjorn ?",
+ "space_blank1": "Aquò es vòstre novèl espaci",
+ "space_blank2": "Lisatz de fichièrs, pegatz de ligams",
+ "space_blank3": "o utilizatz las aisinas",
+ "space_blank4": "Siatz creatius !",
+ "draft": "Borrolhon",
+ "publish": "Publicar",
+ "published": "Publicat",
+ "save_version": "Enregistrar version",
+ "version_saved": "Version enregistrada",
+ "post": "Publicar messatge",
+ "chat_invite_cta1": "Collaboratz amb amusament !",
+ "chat_invite_cta2": "Perqué pas ",
+ "chat_invite_cta3": "convidar de monde",
+ "chat_invite_cta4": "per trabalhar amb vos ?",
+ "chat_message_placeholder": "Escrivètz vòstre messatge…",
+ "view": "Afichatge",
+ "edit": "Edicion",
+ "present": "Present",
+ "chat": "Messatjariá",
+ "meta": "Mèta",
+ "tool_search": "Recercar",
+ "tool_upload": "Enviar",
+ "tool_text": "Tèxte",
+ "tool_shape": "Forma",
+ "tool_zones": "Zònas",
+ "tool_canvas": "Canvas",
+ "search_media": "Cercar de mèdias…",
+ "type_here": "Picatz aquí",
+ "text_formats": "Formats",
+ "format_p": "Paragraph",
+ "format_bullets": "Lista a piuses",
+ "format_numbers": "Lista numeratada",
+ "format_h1": "Títol 1",
+ "format_h2": "Títol 2",
+ "format_h3": "Títol 3",
+ "font_size": "Font Size",
+ "line_height": "Nnautor de linha",
+ "tool_align": "Alinhar",
+ "tool_styles": "Estils",
+ "tool_bullets": "Bullets",
+ "tool_numbers": "Nombres",
+ "color_fill": "Fill",
+ "color_stroke": "Traçat",
+ "color_text": "Tèxte",
+ "tool_type": "Tipe",
+ "tool_box": "Bóstia",
+ "tool_link": "Ligam",
+ "tool_layout": "Agençament",
+ "tool_options": "Opcions",
+ "tool_stroke": "Traçat",
+ "tool_delete": "Suprimir",
+ "tool_lock": "Verrolhar",
+ "tool_copy": "Copiar",
+ "stack": "Pila",
+ "tool_circle": "Cercle",
+ "tool_hexagon": "Exagòn",
+ "tool_square": "Carrat",
+ "tool_diamond": "Diamond",
+ "tool_bubble": "Bulla",
+ "tool_cloud": "Nívol",
+ "tool_burst": "Burst",
+ "tool_star": "Star",
+ "tool_heart": "Còr",
+ "tool_scribble": "Barbolhatge",
+ "tool_line": "Linha",
+ "tool_arrow": "Sageta",
+ "search_media_placeholder": "Cercar de mèdias web…",
+ "add_zone": "Zòna novèla",
+ "palette": "Paleta",
+ "picker": "Pipeta",
+ "background_image_caption": "Imatge",
+ "background_color_caption": "Color",
+ "upload_background_caption": "Clicar per enviar un imatge de rèireplan",
+ "upload_background": "Enviar rèireplan",
+ "access_caption": "Accès",
+ "versions_caption": "Versions",
+ "info_caption": "Info",
+ "mode_private": "Privat : sonque los membres pòdon veire o modificar",
+ "mode_public": "Public : qual que siá amb lo ligam pòt veire",
+ "invite_collaborators": "Convidar collaborators",
+ "revoke_access": "Revocar l’accès",
+ "invite": "Enviar invitacions",
+ "invitee_email_address": "Adreça electronica del novèl membre",
+ "optional_message": "Messatge opcional",
+ "role_viewer": "Visualizaira",
+ "role_editor": "Editor",
+ "role_admin": "Admin",
+ "new_space_title": "Títol novèl per l’Espaci",
+ "team": "Equipa",
+ "search": "Recercar",
+ "search_no_results": "search_no_results",
+ "search_clear": "search_clear",
+ "rename": "Renomenar",
+ "mobile": "mobil",
+ "image": "imatge",
+ "tool_filter": "filtre",
+ "canel": "canel",
+ "invite_membership_action": "invite_membership_action",
+ "viewer": "visualizaira",
+ "editor": "editor",
+ "admin": "admin",
+ "logging_in": "connexion",
+ "password_confirmation": "Confirmacion del senhla",
+ "confirm_again": "Mercés de consultar vòstra bóstia de recepcion per confirmar vòstra adreça.",
+ "confirmed": "Vòstre compte es estat corrèctament confirmat. Mercés.",
+ "signing_up": "Inscripcion",
+ "password_check_inbox": "Verificatz vòstra bóstia de recepcion",
+ "new_space": "Espaci novèl",
+ "tool_more": "Mai",
+ "what_is_your_name": "La benvenguda a %s ! Mercés de causir un escais-nom.",
+ "lang": "en",
+ "landing_title": "Vòstre tablèu blanc sul Web.",
+ "landing_claim": "Spacedeck vos permet de facilament combinar quin que siá tipe de mèdias sus un tablèu virtual : tèxte, nòtas, ligams web, amai vidèos e enregistraments àudio. ",
+ "landing_example": "Lo monde utiliza Spacedeck per organizar lors idèas, en equipa per veire totes los projèctes en una ulhada, a l’escòla e a l’universitat pels mai rics, experiéncia d’aprendissatge connectat.",
+ "spaces": "Mos espacis",
+ "access_editor_link": "Ligam de modificacion dirècta",
+ "access_editor_link_desc": "Donatz aqueste ligam a qualqu’un que deu poder modificar dirèctament aqueste Espaci, cap de compte pas requerit : ",
+ "access_editor_link_desc_slug": "Aqueste ligam conten lo nom de l’espaci, tanben. ",
+ "access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements qu’an creats.",
+ "access_current_members": "Membres actuals",
+ "access_new_members": "Convidar de novèls membres",
+ "access_no_members": "Los membres d’aqueste Espacii apreissaràn aquí.",
+ "comments": "comentaris",
+ "landing_customers": "La fisança de milièr de personas.",
+ "landing_features_title": "Un jòc d'enfants d’utilizar.",
+ "landing_features_text": "Le tout nouveau Spacedeck 5 vous permet de travailler bien plus facilement grâce à sa magnifique interface simplifiée.",
+ "landing_features_1": "Glissez & déposez images, vidéos et audios de votre ordinateur ou du web",
+ "landing_features_2": "Ecrivez directement sur l'espace et choisissez les polices de caractère, couleurs et styles",
+ "landing_features_3": "Dessinez, annotez et surlignez grâce aux formes graphiques intégrées",
+ "landing_features_4": "Transformez votre espace en une présentation dynamique",
+ "landing_features_5": "Collaborez et discutez en temps réel avec vos collègues, élèves et amis",
+ "landing_features_6": "Partagez vos espaces sur le web ou par email",
+ "landing_features_7": "Exportez votre espace en PDF pour l'imprimer",
+ "landing_pricing": "Incroyablement abordable.",
+ "landing_pricing_lite": "Usage personnel",
+ "landing_pricing_lite_text": "La version de base, bien arrondi pour recueillir des images et de garder des notes.",
+ "landing_pricing_pro_features_list": "
Unlimited Spaces
Exporter PDF, ZIP
No Watermarks
Image de fonds
Activity History
20 Go de stockage
",
+ "landing_pricing_pro": "€4,90/User/Mo. €49,90/User/Year",
+ "landing_pricing_pro_text": "Avec toute la puissance que vous attendez.",
+ "landing_pricing_pro_features": "€4,90/User/Mo. €49,90/User/Year",
+ "welcome_subject": "La benvenguda a Spacedeck",
+ "welcome_body": "Mercés per vòstra inscripcion a Spacedeck.\nEsperam qu’auretz plaser a trabalhar dins los Espacis. Oblidetz pas que vòstre compte conten un nombre illimitat de collaborators. Esitetz pas a partejar vòstres espacis amb los amics e collègas del monde entièr.",
+ "invite_emails": "Picatz las adreças mails (separadas per de vergulas)",
+ "history_recently_updated": "Novèlas",
+ "history_recently_empty": "Pas res",
+ "parent_folder": "Repertòri parent",
+ "created_by": "Creat per",
+ "last_updated": "Darrièra mesa a jorn",
+ "feedback_sent": "Comentari enviat",
+ "role_member": "Membre",
+ "team_invite_membership_action": "Acceptar",
+ "space_message_subject": "A publicat sus %s",
+ "space_message_body": "%s a comentat dins %s :\n",
+ "pro_ad_history_headline": "Aprèp una mesa a nivèl podètz obténer un apercebut de totas las activitats actualas dels espacis aquí.",
+ "password_reset_subject": "Reïnicializar lo senhal per Spacedeck",
+ "password_reset_body": "Òu !
Avètz demandat la reïnicializacion del senhal. Mercés de clicar sul ligam seguent per ne causir un novèl. ",
+ "password_reset_action": "Reïnicializar ara",
+ "was_offline": "La connexion a Spacedeck es estada copada. S’avètz de trabalh pas enregistratz, gardatz aqueste onglet de navigador dobèrt fins que la connexion siá restablida puèi tocatz de nòu los elements pas enregistrats.",
+ "subscription_failed_user_subject": "Problèma amb lo pagament Spacedeck",
+ "subscription_failed_user_body": "Unfortunately, we could not process your Payment-method. You can easly create a new payment method including PayPal in your account settings.",
+ "subscription_failed_team_subject": "Problem with your Spacedeck Payment",
+ "subscription_failed_team_body": "Unfortunately, we could not process your Payment-method for your Team-Account. Please fix your payment method asap.",
+ "team_name": "Nom de l’equipa",
+ "subdomain": "jos-domeni",
+ "team_adresses": "Adreças equipa",
+ "add": "Ajustar",
+ "invited": "convidat",
+ "duplicate_destination": "Seleccionatz lo repertòri de destinacion",
+ "duplicate_confirm": "Duplicar %s dins %s ?",
+ "duplicate_success": "%s es estat duplicat dins %s.",
+ "goto_space": "anar a l’espaci",
+ "goto_folder": "anar al repertòri",
+ "stay_here": "Demorar aquí",
+ "sharing": "partatge",
+ "list": "lista",
+ "link": "Ligam",
+ "download_space": "Telecargar espaci",
+ "type": "Tipe",
+ "download": "Telecargar",
+ "Previous Zone": "Zòna precedenta",
+ "Next Zone": "Zòna seguenta",
+ "promote": "Promòure",
+ "demote": "Retrogradar",
+ "more": "Mai",
+ "lock": "Verrolhar",
+ "unlock": "Desverrolhar",
+ "follow_present": "Seguir",
+ "mute_present": "Quitar de seguir",
+ "follow_present_help": "follow_present_help",
+ "export": "exportar"
+}
diff --git a/public/javascripts/spacedeck_account.js b/public/javascripts/spacedeck_account.js
index 06f6846008d316040c9703a31ffbecb42b9f0326..8c28b42cd727adbd2737da911c8bc972537d98ab 100644
--- a/public/javascripts/spacedeck_account.js
+++ b/public/javascripts/spacedeck_account.js
@@ -9,19 +9,12 @@ SpacedeckAccount = {
account_tab: 'invoices',
password_change_error: null,
feedback_text: "",
- importables: [], // spacedeck.com zip import files
},
methods: {
show_account: function() {
this.activate_dropdown('account');
},
- start_zip_import: function(f) {
- if (confirm("Your archive will be imported in the background. This can take a few minutes. You can continue using Spacedeck in the meantime.")) {
- import_zip(this.user, f);
- }
- },
-
account_save_user_digest: function(val) {
this.user.prefs_email_digest = val;
this.save_user(function() {
diff --git a/public/javascripts/spacedeck_routes.js b/public/javascripts/spacedeck_routes.js
index 9d9b3823a56d615cf01efc693635e37662a405d0..8e4fd4abef67f58b063562a52aa270eb2814f931 100644
--- a/public/javascripts/spacedeck_routes.js
+++ b/public/javascripts/spacedeck_routes.js
@@ -17,6 +17,21 @@ var SpacedeckRoutes = {
}.bind(this)
}
]);
+
+ this.router.add([
+ {
+ path: "/s/:hash",
+ handler: function(params, on_success) {
+ var parts = params.hash.split("-");
+ if (path.length > 0) {
+ this.load_space(parts.slice(1).join("-"), on_success, null, parts[0]);
+ } else {
+ // FIXME error handling
+ on_success();
+ }
+ }.bind(this)
+ }
+ ]);
this.router.add([
{
diff --git a/public/javascripts/spacedeck_sections.js b/public/javascripts/spacedeck_sections.js
index 04afb538d500d46bfaff0d365c146957fd7130a5..540d0ae4b06201d14a146089a7650c8a737a0f3f 100644
--- a/public/javascripts/spacedeck_sections.js
+++ b/public/javascripts/spacedeck_sections.js
@@ -63,8 +63,8 @@ var SpacedeckSections = {
active_style: {
border_radius: 0,
stroke: 0,
- font_family: "Avenir W01",
- font_size: 18,
+ font_family: "Inter",
+ font_size: 36,
line_height: 1.5,
letter_spacing: 0,
@@ -110,18 +110,30 @@ var SpacedeckSections = {
color_picker_opacity: 255,
swatches: [
- {id:0, hex:"#4a2f7e"},
- {id:1, hex:"#9b59b6"},
- {id:2, hex:"#3498db"},
- {id:3, hex:"#2ecc71"},
- {id:4, hex:"#f1c40f"},
- {id:5, hex:"#e67e22"},
- {id:6, hex:"#d55c4b"},
- {id:7, hex:"#6f4021"},
- {id:8, hex:"#ffffff"},
- {id:9, hex:"#95a5a6"},
- {id:10, hex:"#252525"},
- {id:11, hex:"rgba(0,0,0,0)"},
+ {id:1, hex:"#ff00ff"},
+ {id:2, hex:"#ffff00"},
+ {id:3, hex:"#00ffff"},
+ {id:5, hex:"#ff0000"},
+ {id:6, hex:"#00ff00"},
+ {id:7, hex:"#0000ff"},
+ {id:8, hex:"#000000"},
+ {id:9, hex:"#222222"},
+ {id:10, hex:"#444444"},
+ {id:11, hex:"#888888"},
+ {id:12, hex:"#bbbbbb"},
+ {id:13, hex:"#dddddd"},
+ {id:14, hex:"#ffffff"},
+
+ {id:20, hex:"#4a2f7e"},
+ {id:21, hex:"#9b59b6"},
+ {id:22, hex:"#3498db"},
+ {id:23, hex:"#2ecc71"},
+ {id:24, hex:"#f1c40f"},
+ {id:25, hex:"#e67e22"},
+ {id:26, hex:"#d55c4b"},
+ {id:27, hex:"#6f4021"},
+ {id:29, hex:"#95a5a6"},
+ {id:30, hex:"rgba(0,0,0,0)"},
],
swatches_text: [
@@ -136,18 +148,8 @@ var SpacedeckSections = {
],
fonts: [
- "Arial",
- "Courier",
- "Georgia",
- "Verdana",
- "Comic Sans MS",
- "Montserrat",
- "Lato",
- "Roboto",
- "Crimson Text",
- "EB Garamond",
- "Vollkorn",
- "Avenir W01"
+ "Inter",
+ "Courier"
],
detected_text_formats: {},
@@ -180,7 +182,7 @@ var SpacedeckSections = {
toolbar_props_in: false,
toolbar_artifacts_x: "-1000px",
toolbar_artifacts_y: "-1000px",
- toolbar_artifacts_in: false
+ toolbar_artifacts_in: true
},
methods: {
@@ -403,7 +405,12 @@ var SpacedeckSections = {
}
if (space.space_type == "folder") return "";
- return "background-image:url('/api/spaces/"+space._id+"/png')";
+ var query_string = "";
+ if (space_auth) {
+ query_string+="?spaceAuth="+space.edit_hash;
+ }
+
+ return "background-image:url('/api/spaces/"+space._id+"/png"+query_string+"')";
},
reset_artifact_filters: function() {
@@ -790,7 +797,6 @@ var SpacedeckSections = {
},
handle_user_cursor_update: function(msg) {
- // console.log("handle cursor", msg);
var now = new Date().getTime();
msg.t = now;
var existing = false;
@@ -802,7 +808,6 @@ var SpacedeckSections = {
u.y = msg.y;
u.t = now;
u.name = msg.name;
- // console.log("updated cursor "+i);
existing = true;
} else {
// hide if no updates since 2sec
@@ -1057,7 +1062,7 @@ var SpacedeckSections = {
this.toolbar_props_x = pp.x+"px";
this.toolbar_props_y = pp.y+"px";
- this.hide_toolbar_artifacts();
+ //this.hide_toolbar_artifacts();
}
this.selection_metrics.x1 = sr.x1;
@@ -1125,9 +1130,12 @@ var SpacedeckSections = {
var er = this.enclosing_rect(this.active_space_artifacts);
if (!er) return;
- this.active_space.width =Math.max(er.x2+100, window.innerWidth);
- this.active_space.height=Math.max(er.y2+100, window.innerHeight);
+ // resize space
+ this.active_space.width =Math.max((parseInt(er.x2/window.innerWidth)+2)*window.innerWidth, window.innerWidth);
+ this.active_space.height=Math.max((parseInt(er.y2/window.innerHeight)+2)*window.innerHeight, window.innerHeight);
+ console.log("bounds: ",this.active_space.width,this.active_space.height);
+
if (this._last_bounds_width != this.active_space.width ||
this._last_bounds_height != this.active_space.height) {
this._last_bounds_width = this.active_space.width;
@@ -1544,7 +1552,7 @@ var SpacedeckSections = {
add_artifact: function (space, item_type, url, evt) {
this.active_tool = "pointer";
this.mouse_state = "idle";
- this.hide_toolbar_artifacts();
+ //this.hide_toolbar_artifacts();
if (!url && (item_type == 'image' || item_type == 'video' || item_type == 'embed')) {
url = prompt("URL?");
@@ -1724,7 +1732,7 @@ var SpacedeckSections = {
var a = {
space_id: this.active_space._id,
mime: "x-spacedeck/shape",
- description: "Text",
+ description: "",
x: point.x,
y: point.y,
z: point.z,
@@ -1736,7 +1744,7 @@ var SpacedeckSections = {
fill_color: "#000000",
shape: shape_type,
valign: "middle",
- align: "center"
+ align: "center",
};
if (this.guest_nickname) {
@@ -1788,9 +1796,7 @@ var SpacedeckSections = {
if (this.active_space_role=="viewer") {
return false;
}
-
- this.hide_toolbar_artifacts();
-
+
// 1. create placeholder artifact
var w=300,h=150;
var fill="transparent";
@@ -2293,11 +2299,6 @@ var SpacedeckSections = {
if (!pastedText) return;
- if (!pastedText.match(/<[a-zA-Z]+>/g)) {
- // crappy heuristic if this is actually HTML
- pastedText = pastedText.replace(/\n/g," ");
- }
-
this.insert_embedded_artifact(pastedText);
},
@@ -2344,32 +2345,6 @@ var SpacedeckSections = {
this.create_artifact_via_embed_url(text);
return;
}
-
- var new_item = {
- mime: "text/html",
- description: text.replace("\n", " "),
- title: "",
- space_id: space._id
- };
-
- var w = 400;
- var h = 300;
- var point = this.find_place_for_item(w,h);
-
- new_item.x = point.x;
- new_item.y = point.y;
- new_item.w = w;
- new_item.h = h;
- new_item.z = point.z;
-
- if (this.guest_nickname) {
- new_item.editor_name = this.guest_nickname;
- }
-
- save_artifact(new_item, function(saved_item) {
- this.update_board_artifact_viewmodel(saved_item);
- this.active_space_artifacts.push(saved_item);
- }.bind(this));
},
create_artifact_via_embed_url: function(url) {
@@ -2528,18 +2503,9 @@ var SpacedeckSections = {
this.opened_dialog = "none";
if (files && files.length) {
- console.log("file: ",files[0]);
-
for (var i=0; i1));
- }
+ this.create_artifact_via_upload(evt, file, (files.length>1));
}
-
} else {
var json = evt.dataTransfer.getData('application/json');
diff --git a/public/javascripts/spacedeck_spaces.js b/public/javascripts/spacedeck_spaces.js
index 8e39893df0da8782939b8afaf65909f1e570c676..f802efa7277a17bb3a59307c54e164f7d4c072ba 100644
--- a/public/javascripts/spacedeck_spaces.js
+++ b/public/javascripts/spacedeck_spaces.js
@@ -18,8 +18,6 @@ var SpacedeckSpaces = {
active_space_path: [],
access_settings_space: null,
access_settings_memberships: [],
- duplicate_folders: [],
- duplicate_folder_id: "",
pending_pdf_files: [],
meta_visible: false,
@@ -101,36 +99,19 @@ var SpacedeckSpaces = {
}.bind(this), {value: dft || "Guest "+parseInt(10000*Math.random()), ok: __("ok"), cancel: __("cancel")});
},
- load_space: function(space_id, on_success, on_error) {
-
- console.log("load space: ", space_id);
+ load_space: function(space_id, on_success, on_error, space_auth) {
this.folder_spaces_filter="";
this.folder_spaces_search="";
- space_auth = get_query_param("spaceAuth");
+ if (space_auth) {
+ set_space_auth(space_auth);
+ } else {
+ set_space_auth(get_query_param("spaceAuth"));
+ }
+
+ this.embedded = !!(get_query_param("embedded"));
var userReady = function() {
- if (get_query_param("embedded")) {
- this.embedded = true;
- this.guest_signup_enabled = true;
- if (get_query_param("publish_cta")) {
- this.publish_cta = get_query_param("publish_cta");
- }
- if (get_query_param("nosocial")) {
- this.social_bar = false;
- }
- }
-
- if (get_query_param("confirm") && this.logged_in) {
- var token = get_query_param("confirm");
- confirm_user(this.user, token, function() {
- this.redirect_to("/spaces/"+space_id+"?show_access=1");
- }.bind(this), function() {
- alert("An error occured confirming your email with the given token.");
- });
- return;
- }
-
this.close_dropdown();
this.active_space_loaded = false;
@@ -158,9 +139,7 @@ var SpacedeckSpaces = {
load_space(space_id, function(space, role) {
document.title = space.name;
- this.active_space_role = role || "viewer"; //via req header from backend
-
- this.space_embed_html = "";
+ this.active_space_role = role || "viewer"; // via req header from backend
if (!is_home) {
load_members(space, function(members) {
@@ -273,9 +252,9 @@ var SpacedeckSpaces = {
this.discover_zones();
- //window.setTimeout(function() {
- // this.zoom_to_fit();
- //}.bind(this),10);
+ window.setTimeout(function() {
+ this.zoom_to_fit();
+ }.bind(this),10);
if (on_success) {
on_success();
@@ -301,15 +280,6 @@ var SpacedeckSpaces = {
// FIXME
this.active_join_link = "";
this.join_link_role = "viewer";
-
- // FIXME
- if (this.active_space_role == "admin") {
- this.space_info_section="access";
- } else if (this.active_space_role == "editor") {
- //this.space_info_section="versions";
- } else {
- this.space_info_section="info";
- }
}
}.bind(this), function(xhr) {
@@ -338,7 +308,7 @@ var SpacedeckSpaces = {
userReady();
}
- if (space_auth) {
+ if (!this.user && space_auth) {
if (this.guest_nickname) {
userReady();
} else {
@@ -673,47 +643,6 @@ var SpacedeckSpaces = {
location.href = "/api/spaces/" + space._id + "/list";
},
- duplicate_space_into_folder: function() {
- load_writable_folders( function(folders){
- this.duplicate_folders = _.sortBy(folders, function (folder) { return folder.name; });
- }.bind(this), function(xhr) {
- console.error(xhr);
- });
- },
-
- duplicate_folder_confirm: function() {
- var folderId = this.duplicate_folder_id;
- var idx = _.findIndex(this.duplicate_folders, function(s) { return s._id == folderId;});
- if (idx<0) idx = 0;
- var folder = this.duplicate_folders[idx];
- console.log("df f",folder);
- if (!folder) return;
-
- duplicate_space(this.active_space, folder._id, function(new_space) {
-
- this.duplicate_folders = [];
- this.duplicate_folder = null;
-
- smoke.quiz(__("duplicate_success", this.active_space.name, folder.name), function(e, test){
- if (e == __("goto_space", new_space.name)){
- this.redirect_to("/spaces/" + new_space._id);
- }else if (e == __("goto_folder", folder.name)){
- this.redirect_to("/folders/" + folder._id);
- }
- }.bind(this), {
- button_1: __("goto_space", new_space.name),
- button_2: __("goto_folder", folder.name),
- button_cancel:__("stay_here")
- });
-
- }.bind(this), function(xhr){
-
- console.error(xhr);
- smoke.prompt("error: " + xhr.statusText);
-
- }.bind(this));
- },
-
toggle_follow_mode: function() {
this.deselect();
this.follow_mode = !this.follow_mode;
@@ -724,6 +653,13 @@ var SpacedeckSpaces = {
this.present_mode = !this.present_mode;
if (this.present_mode) {
//this.go_to_first_zone();
+ if (this.embedded) {
+ document.documentElement.requestFullscreen();
+ }
+ } else {
+ if (this.embedded) {
+ document.exitFullscreen();
+ }
}
},
@@ -819,9 +755,12 @@ var SpacedeckSpaces = {
this.invite_message = "";
}
}.bind(this), function(xhr){
-
- text = JSON.stringify(xhr.responseText);
- smoke.alert("Error: "+text);
+ try {
+ var res = JSON.parse(xhr.response);
+ alert("Error: "+res.error);
+ } catch (e) {
+ console.error(e, xhr);
+ }
}.bind(this));
}.bind(this));
},
@@ -829,9 +768,13 @@ var SpacedeckSpaces = {
update_member: function(space, m, role) {
m.role = role;
save_membership(space, m, function() {
- console.log("saved")
}.bind(this), function(xhr) {
- console.error(xhr);
+ try {
+ var res = JSON.parse(xhr.response);
+ alert("Error: "+res.error);
+ } catch (e) {
+ console.error(e, xhr);
+ }
}.bind(this));
},
@@ -840,7 +783,12 @@ var SpacedeckSpaces = {
delete_membership(space, m, function() {
this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1);
}.bind(this), function(xhr) {
- console.error(xhr);
+ try {
+ var res = JSON.parse(xhr.response);
+ alert("Error: "+res.error);
+ } catch (e) {
+ console.error(e, xhr);
+ }
}.bind(this));
},
@@ -876,10 +824,6 @@ var SpacedeckSpaces = {
}.bind(this));
},
- emojified_comment: function(comment) {
- return twemoji.parse(comment);
- },
-
set_folder_sorting: function(key,reverse) {
this.folder_sorting = key;
this.folder_reverse = reverse?-1:1;
diff --git a/public/javascripts/spacedeck_users.js b/public/javascripts/spacedeck_users.js
index 5b29cd741355d7b4597cd008dd56c5a700db1e3c..3efa13a3ef50754f1a19c2eab2563687b290a264 100644
--- a/public/javascripts/spacedeck_users.js
+++ b/public/javascripts/spacedeck_users.js
@@ -11,12 +11,12 @@ SpacedeckUsers = {
login_email: "",
login_password: "",
signup_password: "",
+ signup_invite_code: "",
signup_password_confirmation: "",
account_remove_error: null,
loading_user: false,
password_reset_confirm_error: "",
password_reset_error: "",
-
},
methods:{
load_user: function(on_success, on_error) {
@@ -30,12 +30,6 @@ SpacedeckUsers = {
if (on_success) {
on_success(user);
}
-
- // see spacedeck_account.js
- load_importables(this.user, function(files) {
- this.importables = files;
- }.bind(this));
-
}.bind(this), function() {
// error
this.loading_user = false;
@@ -122,7 +116,7 @@ SpacedeckUsers = {
signup_guest: function(on_success) {
},
- signup_submit: function($event, name, email, password, password_confirmation, on_success) {
+ signup_submit: function($event, name, email, password, password_confirmation, invite_code, on_success) {
this.creating_user = true;
this.signup_error = null;
@@ -136,7 +130,7 @@ SpacedeckUsers = {
$event.stopPropagation();
}
- create_user(name, email, password, password_confirmation, function(session) {
+ create_user(name, email, password, password_confirmation, invite_code, function(session) {
this.creating_user = false;
this.login_submit(email, password, null, on_success);
}.bind(this), function(req) {
@@ -152,8 +146,8 @@ SpacedeckUsers = {
}.bind(this));
},
- signup_submit_modal: function($event, name, email, password, password_confirmation) {
- this.signup_submit($event, name, email, password, password_confirmation, function() {
+ signup_submit_modal: function($event, name, email, password, password_confirmation, invite_code) {
+ this.signup_submit($event, name, email, password, password_confirmation, invite_code, function() {
alert("Success.");
location.reload();
});
@@ -201,27 +195,29 @@ SpacedeckUsers = {
this.password_reset_confirm_error = null;
this.password_reset_send = false;
- if(password != password_confirmation) {
+ if (password != password_confirmation) {
this.password_reset_confirm_error = "Passwords do not match.";
return;
}
- if(password.length < 5) {
+ if (password.length < 5) {
this.password_reset_confirm_error = "Password too short (must have at least 5 characters).";
return;
}
confirm_password_reset(password, this.reset_token, function(parsed,req) {
- if(req.status==201){
+ if (req.status==201) {
+ alert("New password set successfully.");
this.active_view = "login";
+ } else {
+ alert("An unknown error occured.");
}
}.bind(this), function(req) {
if (req.status==404) {
- var msg = "user not found";
+ alert("Error: Unknown user.");
} else {
- var msg = "error: " + req.statusText;
+ alert("Error: "+req.statusText);
}
- this.password_reset_confirm_error = msg;
}.bind(this));
},
diff --git a/public/javascripts/spacedeck_vue.js b/public/javascripts/spacedeck_vue.js
index 141cb1ae0d5d4f060fa8b3147a70254bd641bb0d..45bed31a3607ca65e720fc06235930130a587af8 100644
--- a/public/javascripts/spacedeck_vue.js
+++ b/public/javascripts/spacedeck_vue.js
@@ -14,6 +14,7 @@ function boot_spacedeck() {
account: "profile",
logged_in: false,
guest_nickname: null,
+ embedded: false,
user: {},
active_profile: null,
diff --git a/public/javascripts/spacedeck_whiteboard.js b/public/javascripts/spacedeck_whiteboard.js
index 9bdb4ec962e6fb7dbd10269db3ab50730e3cb33a..7c53f14b652b5a744d41c0ceb38e1bc1a55ef1c7 100644
--- a/public/javascripts/spacedeck_whiteboard.js
+++ b/public/javascripts/spacedeck_whiteboard.js
@@ -5,7 +5,10 @@
*/
function setup_whiteboard_directives() {
+ var mode_touch = false;
+
if ('ontouchstart' in window) {
+ mode_touch = true;
var edown = "touchstart";
var emove = "touchmove";
var eup = "touchend";
@@ -15,6 +18,12 @@ function setup_whiteboard_directives() {
var eup = "mouseup";
}
+ // detect first touch event
+ window.addEventListener('touchstart', function on_first_touch() {
+ mode_touch = true;
+ window.removeEventListener('touchstart', on_first_touch, false);
+ }, false);
+
Vue.directive('sd-whiteboard', {
bind: function () {
var el = this.el;
@@ -23,9 +32,12 @@ function setup_whiteboard_directives() {
$(el).on("dblclick", ".artifact", this.handle_double_click_artifact.bind(this));
$(el).on("keyup", ".artifact", this.handle_key_up_artifact.bind(this));
$(el).on("keydown", ".artifact", this.handle_key_down_artifact.bind(this));
- $(el).bind(edown, this.handle_mouse_down_space.bind(this));
- $(el).bind(emove, this.handle_mouse_move.bind(this));
- $(el).bind(eup, this.handle_mouse_up_space.bind(this));
+ $(el).bind("touchstart", this.handle_mouse_down_space.bind(this));
+ $(el).bind("touchmove", this.handle_mouse_move.bind(this));
+ $(el).bind("touchend", this.handle_mouse_up_space.bind(this));
+ $(el).bind("mousedown", this.handle_mouse_down_space.bind(this));
+ $(el).bind("mousemove", this.handle_mouse_move.bind(this));
+ $(el).bind("mouseup", this.handle_mouse_up_space.bind(this));
$(el).bind("wheel", this.handle_wheel_space.bind(this));
@@ -80,10 +92,16 @@ function setup_whiteboard_directives() {
evt.stopPropagation();
}
- var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
-
if ($scope.active_tool == "zoom") return;
+ if (evt.which == 2) {
+ // middle mouse button
+ this.handle_mouse_down_space(evt);
+ return;
+ }
+
+ var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
+
if ($scope.active_tool == "eyedrop") {
var arts = $scope.selected_artifacts();
if (!$scope.is_selected(a) && arts.length > 0) {
@@ -196,8 +214,10 @@ function setup_whiteboard_directives() {
},
handle_mouse_down_space: function(evt) {
- if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
-
+ if (evt.which != 2) {
+ if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
+ }
+
var $scope = this.vm.$root;
$scope.opened_dialog="none";
@@ -206,7 +226,7 @@ function setup_whiteboard_directives() {
$scope.mouse_ox = cursor.x;
$scope.mouse_oy = cursor.y;
- if (evt.which == 2 || evt.buttons == 4) {
+ if ((mode_touch && $scope.active_tool=="pointer") || evt.which == 2 || evt.buttons == 4) {
$scope.active_tool = "pan";
}
@@ -214,7 +234,7 @@ function setup_whiteboard_directives() {
this.deselect();
this.mouse_state = "transform";
$scope.mouse_state = this.mouse_state;
- this.start_adding_note(evt);
+ this.start_drawing_note(evt);
return;
} else if ($scope.active_tool=="arrow") {
@@ -354,12 +374,10 @@ function setup_whiteboard_directives() {
var lasso_scaled = {
x:this.lasso.x,
y:this.lasso.y,
- w:this.lasso.w*$scope.viewport_zoom,
- h:this.lasso.h*$scope.viewport_zoom
+ w:this.lasso.w,
+ h:this.lasso.h
}
lasso_scaled = this.abs_rect(lasso_scaled);
- lasso_scaled.x += $scope.bounds_margin_horiz;
- lasso_scaled.y += $scope.bounds_margin_vert;
var s = "left:" +lasso_scaled.x+"px;";
s += "top:" +lasso_scaled.y+"px;";
@@ -379,15 +397,15 @@ function setup_whiteboard_directives() {
$("#lasso").show();
},
+ // Translate the mouse cursor location from device window coordinates to virtual space coordinates
cursor_point_to_space: function(evt) {
var $scope = this.vm.$root;
- var offset = {left: 0, top: 0};
evt = fixup_touches(evt);
return {
- x: (parseInt(evt.pageX) - parseInt(offset.left) - $scope.bounds_margin_horiz) / this.space_zoom,
- y: (parseInt(evt.pageY) - parseInt(offset.top) - $scope.bounds_margin_vert) / this.space_zoom
+ x: $scope.scroll_left + (parseInt(evt.pageX) - $scope.bounds_margin_horiz) / $scope.viewport_zoom,
+ y: $scope.scroll_top + (parseInt(evt.pageY) - $scope.bounds_margin_vert) / $scope.viewport_zoom
};
},
@@ -492,6 +510,7 @@ function setup_whiteboard_directives() {
if (!xdists[0] || xdists[0][0]>TOL) {
results.snapx = [0,x]; // distance, coordinate
} else {
+ // FIXME snap rulers are broken
//$scope.snap_ruler_x = xdists[0][1];
}
if (!ydists[0] || ydists[0][0]>TOL) {
@@ -503,17 +522,38 @@ function setup_whiteboard_directives() {
return results;
},
- offset_point_in_wrapper: function(point) {
- var $scope = this.vm.$root;
- var section_el = $(this.el)[0];
- var z = $scope.viewport_zoom;
+ start_drawing_note: function(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
- var pt = parseInt($("#space").css("padding-top"));
+ var $scope = this.vm.$root;
+ var point = this.cursor_point_to_space(evt);
+ var z = $scope.highest_z()+1;
- point.y=(point.y+section_el.scrollTop-pt)/z;
- point.x=(point.x+section_el.scrollLeft)/z;
+ var a = {
+ space_id: $scope.active_space._id,
+ mime: "text/html",
+ description: "
+ Whenever you need to lay out pictures, text notes, video and audio clips on a blank canvas,
+ Spacedeck can help you.
+
+
+ Spacedeck is a browser based application. It is the right tool for you if you want to quickly put together a collage of your idea or concept, either for yourself or to share it with teammembers, clients or students. Changes are updated in realtime.
+
+
+ Spacedeck is not meant for creating polished designs, but it is a good fit for:
+