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("

payload:

");
+	// print_r($payload);
+	// echo("
"); + + $result = wp_remote_post($url, $payload); + + if (is_wp_error($result)) { + return $result; + } + + $result = json_decode($result[body], true); + + // echo("

decoded:

");
+	// print_r($result);
+	// echo("
"); + + return $result; +} + +function spacedeck_embed_space($slug, $width = '90%', $height = '800', $parent_space_id = null) { + $spacedeck_frontend_base_uri = get_option("spacedeck_settings")[spacedeck_frontend_base_uri]; + + // try to find the space identified by slug + $space = spacedeck_apicall("GET", "/spaces/" . $slug, array()); + + if (is_wp_error($space)) { + $error = $response->get_error_message(); + return("

Spacedeck: WP Error looking up Space: $error

"); + } else if ($space[error] && $space[error]!="space_not_found") { + return("

Spacedeck: Error looking up Space: $space[error]

"); + } + + // if it doesn't exist, create it: + if ($space[error]=="space_not_found") { + $data = array( + "name" => $slug, + "edit_slug" => $slug + ); + + if ($parent_space_id) { + $data[parent_space_id] = $parent_space_id; + } + + $space = spacedeck_apicall("POST", "/spaces", $data); + + if (is_wp_error($space)) { + $error = $response->get_error_message(); + return("

Spacedeck: WP Error creating Space: $error

"); + } else if ($space[error]) { + return("

Spacedeck: Error creating Space: $space[error]

"); + } + } + + if (is_wp_error($space)) { + $error = $response->get_error_message(); + return("

Spacedeck: WP Error embedding Space: $error

"); + } else if (!$space || $space[error]) { + return("

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": "