55 Commits

Author SHA1 Message Date
mntmn
9a1e85fb94 space: fix realtime artifact media conversion feedback for video, audio 2020-11-21 16:38:17 +01:00
mntmn
89f48e615f remove unnecesary background image from video element 2020-11-10 21:21:40 +01:00
mntmn
8b5aaed92a fix #50: Leave property panels open when selecting another element 2020-11-10 21:05:02 +01:00
mntmn
0eac7749cd gardening: remove trailing whitespace in spacedeck_sections.js 2020-11-10 21:04:29 +01:00
mntmn
fb217f358a Merge branch 'mnt' of github.com:spacedeck/spacedeck-open into mnt 2020-11-10 20:55:02 +01:00
banglashi
e081bcd6fe top-left align thumbs instead of bg cover (#111)
* top-left align thumbs instead of bg cover

* compile styles
2020-11-10 20:54:53 +01:00
mntmn
819f1fecb3 Merge branch 'mnt' of github.com:spacedeck/spacedeck-open into mnt 2020-11-10 20:54:03 +01:00
nik gaffney
4387e336bb docs: add apache config (#108) (#121) 2020-11-10 20:47:16 +01:00
banglashi
5fcc4866bd fix upload progress comparison (#122) 2020-11-10 20:46:32 +01:00
Julian David
4c7ab4df02 fix #99 - Changing attributes (like color) should not disable current… (#120)
* fix #99 - Changing attributes (like color) should not disable current tool

* apply review: remove unnecessary line
2020-11-10 18:19:58 +01:00
Ratul sharker
f7453b3977 Data type TEXT used for artifact.shape_svg. (#115) 2020-11-10 18:00:59 +01:00
Julian David
121997ef3e Correct misspelled words in Spanish and add tooltips to main buttons canvas. (#118)
* Correct misspelled words in Spanish

* add tooltips to the main buttons of the canvas.
2020-11-10 17:57:08 +01:00
juliandavidmr
16f732a50c add tooltips to the main buttons of the canvas. 2020-11-09 19:53:17 -05:00
juliandavidmr
096482d7eb Correct misspelled words in Spanish 2020-11-09 18:51:16 -05:00
banglashi
6257e3410e fix pdf export + add download_as_pdf template string and action (#110)
* fix pdf export
add download_as_pdf template string and action

* after pdf export click
- close dropdown
- open in new tab

* proper fix function
2020-11-02 20:07:15 +01:00
mntmn
7c6d5c83db space: deselect when using tools; normalize note tool behavior 2020-09-28 00:09:18 +02:00
mntmn
2a2c7011c0 space: fix artifact z-stacking tool 2020-09-28 00:00:04 +02:00
mntmn
32323cbc57 space: fix initial position for new shapes 2020-09-27 23:54:22 +02:00
mntmn
cbd0034be1 space: fix creating notes on top of other elements 2020-09-27 23:47:53 +02:00
mntmn
b3927e0646 space: deselect and quit current tool with escape key 2020-09-27 23:39:33 +02:00
mntmn
d4407556ca space/notes: default to more useful note size and padding 2020-09-27 23:32:26 +02:00
mntmn
52ff6e351b arrow/scribble: default to active_style's stroke color 2020-09-27 23:25:06 +02:00
mntmn
137d17269a space: fix space object updating and fix background update response 2020-09-22 15:39:50 +02:00
mntmn
81a45bb0bd spacedeck: background: fix bg picture preview size 2020-09-22 15:25:50 +02:00
mntmn
7dd3ceffc4 remove some unused config options 2020-09-22 15:23:11 +02:00
mntmn
b60cd0ffa7 Merge branch 'mnt' of github.com:spacedeck/spacedeck-open into mnt 2020-09-22 15:22:08 +02:00
mntmn
bd8667feba space: fix background image attachment 2020-09-22 15:21:39 +02:00
Omid
a6616f3463 Add missing config for s3 storage (#96)
This var `storage_endpoint` will be read and used at 3eb99d2635/helpers/uploader.js (L14)
2020-09-19 19:12:51 +02:00
nblock
3eb99d2635 Make host and port configurable (#94)
Use the previous values as the default.
2020-09-16 11:20:13 +02:00
mntmn
09b42f24c2 remove stray character in toolbar template 2020-09-16 11:02:07 +02:00
mntmn
a8b8e36ad3 wip: migrate from deprecated swig templates to ejs 2020-09-09 17:20:35 +02:00
mntmn
b4f0fa16ef wip: remove basic-auth (unused) 2020-09-09 16:24:30 +02:00
Mishkin Berteig
dccf0465b3 Fix to issue #71 Cursor positions on shared whiteboards are inconsistent (#86)
* Fixed "Cursor Positions on Shared Whiteboards are Inconsistent".

The main fix is in the method cursor_point_to_space in
public/javascripts/spacedeck_whiteboard.js.

The calculation of the coordinates of the mouse pointer from absolute
window to the whiteboard space coordinates was incorrect.

There were a number of dependencies on this method which were updated
as a result.

One side-effect was that the div for the lasso tool needed to be
moved inside the div for the whiteboard.

* Fixed minor panning calculation problem.  Works now!
2020-09-09 16:11:43 +02:00
foramontano
7f72992d06 Foramontano/adding languages (#89)
- Doc that describes the steps to add a new language  to Spacedeck Open
- New file with translated entries for using in Spacedeck Open in Spanish
- Include 'es' as new locale in Spacedeck Open
- Include a radiobutton for users could select the new language
2020-09-09 16:05:55 +02:00
Mishkin Berteig
ff1da80695 Docs Folder (#84)
* Initial documentation folder with instructions on adding fonts.

* fixed markdown syntax error

* Update adding_fonts.md

Added some links to the referred code lines and fixed some formatting.

Co-authored-by: mntmn <lukas@mnt.mn>
2020-07-25 21:38:06 +02:00
yrammos
83928dee3b Fix #58 ('duplicate column name' on fresh install). (#81) 2020-07-24 01:35:43 +02:00
Mishkin Berteig
28f4c5d58b Fixed star and starburst objects so that they render properly within… (#79)
* Fixed star and starburst objects so that they render properly withing the selection bounding box. Lots of math. Still to do is to handle situations when the bounding box is very small and the stroke width of the star[burst] makes the interior corners overlap.  Currently causes weird rendering.

* Fixed some minor typos and formatting errors in the comments.  No changes to executable code.
2020-07-24 01:35:18 +02:00
mntmn
6e4685e589 Update README.md 2020-07-22 22:01:40 +02:00
Mejans
9c44aee57a i18n Add Occitan language (#80)
* Create oc.js

* Add OC to locale list

* Add OC

* Add OC to list

* Update spacedeck.html

* Update root.js
2020-07-22 21:55:32 +02:00
togir
c491af64d8 Keep the colors between drawing multiple scribbles and arrows (#61)
* Keep the colors between drawing multiple scribbles and arrows

The color is changed back to black on every new drawing. Now the color is kept. If there is no color is set, then there is a fallback to the black stroke as before.

* requested changes

Co-authored-by: togir <gitcatch@weiler.rocks>
2020-07-22 21:53:51 +02:00
Florian Kohrt
6bbeb4383d fix #60: add ffmpeg package (#66) 2020-06-24 15:26:34 +02:00
mntmn
0c5fa597e8 Allow embedding of folders and access to folders to anonymous editors with edit_hash/spaceAuth links (#63)
* add subspaces to be listed with edit_hash/spaceAuth authorization

* remove dead code from api_helpers.js

* add edit_hash authorization for requested space thumbnails

* handle /s/:hash links in frontend router

* set space_auth via a function, allow passing it to load_space

* rename variable in /s/:hash router in backend

* hide search, profile, breadcrumb in folders if not logged in, construct links to subspaces differently for anonymous editors
2020-06-02 20:47:58 +02:00
P. J. Reed
8ddbec6b68 Allow anonymous SMTP connections (#59)
This adds support for anonymously connecting to SMTP servers
that do not require authentication; if a username is not provided,
it will not attempt to authenticate.
2020-05-19 22:21:07 +02:00
Florian Kohrt
3ce40c51a6 Add docker files (#55)
* Add docker files

* Improve docker-compose compatibility

* Use Openstack's standard port

* Use package ffmpeg-dev instead of ffmpeg

* improve dockerfile and config

* Add short docker guide

* adhere to previous naming conventions

* document former json comments

* Undo init.sh approach to touching the db file

* Make clear that it's about files on the host system

* add local path specifier

Co-authored-by: Denys Vitali <denys.vitali@swisscom.com>
2020-05-19 22:19:43 +02:00
mntmn
077bf165c7 integrations: first version of Spacedeck integration for WordPress 2020-05-11 18:58:02 +02:00
mntmn
e5fc11d2cd update styles 2020-05-11 18:58:02 +02:00
mntmn
a185c1e3a6 helmet: disable protections for now 2020-05-11 18:58:02 +02:00
mntmn
effeb6c809 security: prevent leak of creator information in space responses; ensure home folder id is set when creating space 2020-05-11 18:58:02 +02:00
mntmn
e61bc1e23f remove space border 2020-05-11 18:58:02 +02:00
mntmn
051821e26d hide home button in embedded mode 2020-05-11 18:58:02 +02:00
mntmn
0a4399951a when space is embedded (?embedded=1), toggle fullscreen via present button 2020-05-11 18:58:02 +02:00
mntmn
f6cfef899e allow looking up spaces via slug 2020-05-11 18:58:02 +02:00
mntmn
b99ec300bb allow auth via api_token 2020-05-11 18:58:02 +02:00
mntmn
b93cc20371 users: add api_token attribute, make editable in profile/account 2020-05-11 18:58:02 +02:00
Dorthe Luebbert
46769691d8 Added Graphicsmagick as dependency (#52) 2020-05-11 18:55:13 +02:00
73 changed files with 1883 additions and 789 deletions

38
Dockerfile Normal file
View File

@@ -0,0 +1,38 @@
FROM node:10-alpine3.11
WORKDIR /app
# 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
# install other requirements
RUN apk add graphicsmagick ffmpeg ffmpeg-dev ghostscript
# install node package
COPY package*.json ./
RUN npm install
COPY . .
# start app
EXPOSE 9666
CMD ["node", "spacedeck.js"]

View File

@@ -32,6 +32,8 @@ We appreciate filed issues, pull requests and general discussion.
Spacedeck requires:
- 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 10.x.
@@ -41,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)
@@ -62,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:

View File

@@ -2,28 +2,24 @@
"team_name": "My Open Spacedeck",
"contact_email": "support@example.org",
"host": "::",
"port": 9666,
"endpoint": "http://localhost:9666",
"invite_code": "top-sekrit",
"storage_local_path": "./storage",
"storage_local_db": "./database.sqlite",
"storage_region": "eu-central-1",
//"storage_bucket": "sdeck-development",
//"storage_cdn": "http://localhost:9123/sdeck-development",
//"storage_endpoint": "http://storage:9000",
"storage_endpoint": "http://localhost:4572",
"storage_bucket": "my_spacedeck_bucket",
"storage_cdn": "/storage",
"storage_local_path": "./storage",
"redis_mock": true,
"mongodb_host": "localhost",
"redis_mock": true,
"redis_host": "localhost",
"google_access" : "",
"google_secret" : "",
"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,

12
docker-compose.yml Normal file
View File

@@ -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

6
docs/adding_fonts.md Normal file
View File

@@ -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.

26
docs/adding_languages.md Normal file
View File

@@ -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" %>;
</script>
```
- Include a radio button for users to select the new language (/views/partials/account.html)
```
<label class="radio" v-bind:class="{checked: user.prefs_language=='es'}" v-on:click="save_user_language('es')">
<input type="radio" id="user-preferences_language" name="language" value="es"><span>Español</span>
</label>
```

51
docs/apache_setup.md Normal file
View File

@@ -0,0 +1,51 @@
# Apache as reverse proxy
Once spacedeck is running you can use Apache as a reverse proxy. For a general overview of how this works or how to configure specifics the [Apache Reverse Proxy Guide](https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html) provides a good reference.
If you have the required modules and ssl certificates installed skip to the site config.
# Required modules
Install `mod_rewrite`, `mod_proxy` and `mod_proxy_wstunnel`
```
sudo a2enmod proxy rewrite proxy_wstunnel
```
# SSL certificates
Set up `certbot` frmo [letsencrypt](https://letsencrypt.org/) (if required) and get some certs…
```
sudo certbot --apache certonly -n -d space.example.net
```
# Site config
This config should work with Apache 2.4 and assumes spacedeck is running on localhost using port 9666, that `mod_rewrite`, `mod_proxy` and `mod_proxy_wstunnel` are active, and that ssl certificates have been installed with `certbot`.
```
<VirtualHost *:443>
ServerName space.example.net
ServerAdmin webmaster@space.example.net
ErrorLog /var/log/apache2/spacedeck-error.log
CustomLog /var/log/apache2/spacedeck-access.log combined
# ssl options
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/space.example.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/space.example.net/privkey.pem
# proxy spacedeck
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:9666/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:9666/$1 [P,L]
ProxyPassReverse / http://localhost:9666/
</VirtualHost>
```

View File

@@ -82,7 +82,7 @@ function createWaveform(fileName, localFilePath, callback){
"-i", localFilePath, "-o", filePathImage
],
{}, function(error, stdout, stderr) {
if(!error) {
if (!error) {
callback(null, filePathImage);
} else {
console.log("error:", stdout, stderr);
@@ -99,7 +99,7 @@ function convertVideo(fileName, filePath, codec, callback, progressCallback) {
var newExt = codec == "mp4" ? "mp4" : "ogv";
var convertedPath = filePath + "." + newExt;
console.log("converting", filePath, "to", convertedPath);
console.log("convertVideo", filePath, "to", convertedPath);
var convertArgs = (codec == "mp4") ? [
"-i", filePath,
@@ -229,7 +229,6 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
}
}
var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) {
async.parallel({
small: function(callback){
@@ -281,7 +280,7 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
module.exports = {
convert: function(a, fileName, localFilePath, payloadCallback, progressCallback) {
getMime(fileName, localFilePath, function(err, mimeType){
getMime(fileName, localFilePath, function(err, mimeType) {
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
if (!err) {
@@ -291,7 +290,7 @@ module.exports = {
console.log("[convert] gm:", err, size);
if (!err) {
if(mimeType == "application/pdf") {
if (mimeType == "application/pdf") {
var firstImagePath = localFilePath + ".jpeg";
exec.execFile("gs", ["-sDEVICE=jpeg","-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-dFirstPage=1", "-dLastPage=1", "-sOutputFile=" + firstImagePath, "-r90", "-f", localFilePath], {}, function(error, stdout, stderr) {
if(error === null) {
@@ -305,7 +304,7 @@ module.exports = {
}
});
} else if(mimeType == "image/gif") {
} else if (mimeType == "image/gif") {
//gifs are buggy after convertion, so we should not convert them
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
@@ -334,7 +333,7 @@ module.exports = {
a.save().then(function() {
fs.unlink(localFilePath, function (err) {
if (err){
if (err) {
console.error(err);
payloadCallback(err, null);
} else {
@@ -453,11 +452,10 @@ module.exports = {
});
} else if (convertableAudioTypes.indexOf(mimeType) > -1) {
async.parallel({
ogg: function(callback) {
convertAudio(fileName, localFilePath, "ogg", function(err, file) {
if(err) callback(err);
if (err) callback(err);
else {
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".ogg" ;
uploader.uploadFile(keyName, "audio/ogg", file, function(err, url){
@@ -469,20 +467,16 @@ module.exports = {
},
mp3_waveform: function(callback) {
convertAudio(fileName, localFilePath, "mp3", function(err, file) {
if(err) callback(err);
if (err) callback(err);
else {
createWaveform(fileName, file, function(err, filePath){
createWaveform(fileName, file, function(err, filePath) {
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + "-" + (new Date().getTime()) + ".png";
uploader.uploadFile(keyName, "image/png", filePath, function(err, pngUrl){
uploader.uploadFile(keyName, "image/png", filePath, function(err, pngUrl) {
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".mp3" ;
uploader.uploadFile(keyName, "audio/mp3", file, function(err, mp3Url){
uploader.uploadFile(keyName, "audio/mp3", file, function(err, mp3Url) {
if (err) callback(err);
else callback(null, {waveform: pngUrl, mp3: mp3Url});
});
});
});
}
@@ -490,7 +484,7 @@ module.exports = {
},
original: function(callback) {
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url){
uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url) {
callback(null, url);
});
}
@@ -499,7 +493,6 @@ module.exports = {
if (err) payloadCallback(err, a);
else {
a.state = "idle";
a.mime = mimeType;
var stats = fs.statSync(localFilePath);
@@ -515,10 +508,9 @@ module.exports = {
];
a.updated_at = new Date();
db.packArtifact(a);
a.save().then(function(){
a.save().then(function() {
fs.unlink(localFilePath, function (err) {
if (err){
console.error(err);
@@ -559,5 +551,3 @@ module.exports = {
});
}
};

View File

@@ -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,18 +22,14 @@ module.exports = {
plaintext+="\n"+options.action.link+"\n\n";
}
const htmlText = swig.renderFile('./views/emails/action.html', {
text: body.replace(/(?:\n)/g, '<br />'),
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({
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'),
@@ -45,14 +39,21 @@ module.exports = {
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);

View File

@@ -173,6 +173,7 @@
"tool_styles": "Stil",
"tool_bullets": "Bullets",
"tool_numbers": "Zahlen",
"tool_font": "Font",
"color_fill": "Füllung",
"color_stroke": "Strich",
"color_text": "Text",
@@ -303,6 +304,7 @@
"sharing": "sharing",
"list": "Liste",
"download_space": "Space Herunterladen",
"download_as_pdf": "Space als PDF herunterladen",
"duplicate_destination_folder": "Zielordner für Duplikat",
"type": "Typ",
"promote": "Befördern",
@@ -317,5 +319,6 @@
"more": "mehr",
"follow_present": "Folgen",
"mute_present": "Entfolgen",
"follow_present_help": "Wenn jemand den Space präsentiert, folgen die anderen Mitglieder automatisch der Präsentation. Mit diesem Knopf lässt sich das an- oder ausschalten."
"follow_present_help": "Wenn jemand den Space präsentiert, folgen die anderen Mitglieder automatisch der Präsentation. Mit diesem Knopf lässt sich das an- oder ausschalten.",
"media": "Media"
}

View File

@@ -172,6 +172,7 @@
"tool_styles": "Styles",
"tool_bullets": "Bullets",
"tool_numbers": "Numbers",
"tool_font": "Font",
"color_fill": "Fill",
"color_stroke": "Stroke",
"color_text": "Text",
@@ -308,6 +309,7 @@
"list": "Export List",
"link": "Link",
"download_space": "Download Space",
"download_as_pdf": "Download Space as PDF",
"type": "Type",
"download": "Download",
"Previous Zone": "Previous Zone",
@@ -320,5 +322,6 @@
"follow_present": "Follow",
"mute_present": "Unfollow",
"follow_present_help": "If someone else is presenting this Space, the other members automatically follow the presentation. Switch following on or off with this button.",
"export": "export"
"export": "export",
"media": "Media"
}

327
locales/es.js Normal file
View File

@@ -0,0 +1,327 @@
{
"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": "Cargar 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 comentarios",
"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": "Restablecer 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údanos 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 colaboració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",
"tool_font": "Fuente",
"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óvil",
"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 Instantánea",
"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": "<b>Arrastra & suelta</b> imágenes, vídeos y áudios desde tu computadora o desde la web",
"landing_features_2": "<b>Escribe texto y formatéalo</b> con pleno control sobre fuente, color y estilo",
"landing_features_3": "<b>Dibuja, anota y resalta</b> incluyendo contornos gráficos",
"landing_features_4": "Convierte tu tablero en un <b>área de presentación con zoom</b>",
"landing_features_5": "<b>Colabora y chatea</b> en tiempo real con compañeros de equipo, alumnos y amigos.",
"landing_features_6": "<b>Comparte Espacios</b> en la web o via correo electrónico",
"landing_features_7": "<b>Exporta tu trabajo</b> 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": "<ul><li>Espacios ilimitados</li><li>Estructura de Directorios</li><li>Exportación a ficheros PDF y ZIP</li><li>Sin Marcas de Agua</li><li>Personaliza tu fondo</li><li>Historial de Actividad</li><li>20 GB de Almacenamiento</li><ul>",
"landing_pricing_pro": "€4,90/Usuario/Mes <br><small>o 49,90/Usuario/Año</small>",
"landing_pricing_pro_text": "Con toda 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.<br>Esperamos que disfrutes trabajando con Espacios.<br>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 interrumpido. Si tienes trabajo sin salvar, mantén abierta, por favor, esta pestaña del navegador hasta 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 el 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",
"download_as_pdf": "Descargar espacio en PDF",
"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",
"media": "Media"
}

View File

@@ -172,6 +172,7 @@
"format_h3": "Titre 3",
"font_size": "Taille de la police",
"line_height": "Hauteur de ligne",
"tool_font": "Font",
"tool_align": "Align",
"tool_styles": "Style",
"tool_bullets": "Puces",
@@ -301,6 +302,7 @@
"goto_folder": "Aller au dossier %s",
"stay_here": "Reste ici",
"download_space": "télécharger un espace",
"download_as_pdf": "télécharger un espace comme PDF",
"type": "Type",
"Previous Zone": "Zone précédent",
"Next Zone": "Zone suivante",
@@ -314,5 +316,6 @@
"more": "plus",
"follow_present": "Suivre",
"mute_present": "Pas suivre",
"follow_present_help": "follow_present_help"
"follow_present_help": "follow_present_help",
"media": "Media"
}

327
locales/oc.js Normal file
View File

@@ -0,0 +1,327 @@
{
"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": "Sinscriure",
"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 despecificar",
"confirm": "Mercés de confirmar",
"error_unknown_email": "Aquesta combinason dadreç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 dopcions.",
"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 dequipa per %s",
"team_invite_membership_body": "Qualquun vos a convidat a %s sus Spacedeck. Mercés de clicar sul ligam seguent per acceptar linvitacion.",
"team_invite_user_body": "Qualquun vos a convidat a %s sus Spacedeck.\nVòstre senhal temporari es « %s».\nMercés de clicar sul ligam seguent per acceptar linvitacion.",
"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 lequipa %s sus Spacedeck",
"space_invite_membership_subject": "Invitacion Espaci per %s: %s",
"space_invite_membership_body": "%s vos a convit a lEspaci « %s»",
"space_invite_membership_action": "Acceptar linvitacion",
"folder_invite_membership_subject": "Espaci",
"folder_invite_membership_body": "Qualquun vos a convidat a Team sus Spacedeck. Clicatz lo ligam seguent per acceptar linvitacion.",
"folder_invite_membership_acction": "Acceptar",
"login_google": "Sidentificar 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 despacis.",
"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: 200x200pixè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 quexplica 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",
"tool_font": "Font",
"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 laccè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 lEspaci",
"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 lescòla e a luniversitat pels mai rics, experiéncia daprendissatge connectat.",
"spaces": "Mos espacis",
"access_editor_link": "Ligam de modificacion dirècta",
"access_editor_link_desc": "Donatz aqueste ligam a qualquun que deu poder modificar dirèctament aqueste Espaci, cap de compte pas requerit: ",
"access_editor_link_desc_slug": "Aqueste ligam conten lo nom de lespaci, tanben. ",
"access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements quan creats.",
"access_current_members": "Membres actuals",
"access_new_members": "Convidar de novèls membres",
"access_no_members": "Los membres daqueste Espacii apreissaràn aquí.",
"comments": "comentaris",
"landing_customers": "La fisança de milièr de personas.",
"landing_features_title": "Un jòc d'enfants dutilizar.",
"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": "<ul><li>Unlimited Spaces</li><li>Exporter PDF, ZIP</li><li>No Watermarks</li><li>Image de fonds</li><li>Activity History</li><li>20 Go de stockage</li><ul>",
"landing_pricing_pro": "€4,90/User/Mo. <br><small> €49,90/User/Year</small>",
"landing_pricing_pro_text": "Avec toute la puissance que vous attendez.",
"landing_pricing_pro_features": "€4,90/User/Mo. <br><small> €49,90/User/Year</small>",
"welcome_subject": "La benvenguda a Spacedeck",
"welcome_body": "Mercés per vòstra inscripcion a Spacedeck.\nEsperam quauretz plaser a trabalhar dins los Espacis. <br> Oblidetz pas que vòstre compte conten un nombre illimitat de collaborators. <br> 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!<br><br>Avètz demandat la reïnicializacion del senhal.<br>Mercés de clicar sul ligam seguent per ne causir un novèl.<br>",
"password_reset_action": "Reïnicializar ara",
"was_offline": "La connexion a Spacedeck es estada copada. Savè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 lequipa",
"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 lespaci",
"goto_folder": "anar al repertòri",
"stay_here": "Demorar aquí",
"sharing": "partatge",
"list": "lista",
"link": "Ligam",
"download_space": "Telecargar espaci",
"download_space_as_pdf": "Telecargar espaci PDF",
"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",
"media": "Media"
}

View File

@@ -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,24 @@ 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) {
res['distributeUpdate'] = function(model, object, sendToSelf) {
if (!object) return;
if (sendToSelf) {
// send this update to the initiating user, for example when
// a conversion task has finished
redis.sendMessage("update-self", model, object, req.channelId);
} else {
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();

View File

@@ -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
@@ -174,7 +175,7 @@ module.exports = {
crop_w: Sequelize.INTEGER,
crop_h: Sequelize.INTEGER,
shape: Sequelize.STRING,
shape_svg: Sequelize.STRING,
shape_svg: Sequelize.TEXT,
padding_left: Sequelize.INTEGER,
padding_right: Sequelize.INTEGER,
padding_top: Sequelize.INTEGER,

View File

@@ -3,7 +3,7 @@
module.exports = {
up: function(migration, DataTypes) {
return Promise.all([
migration.addColumn('users', 'api_token',
migration.changeColumn('users', 'api_token',
{
type: DataTypes.STRING
}
@@ -13,7 +13,11 @@ module.exports = {
down: function(migration, DataTypes) {
return Promise.all([
migration.removeColumn('users', 'api_token')
migration.changeColumn('users', 'api_token',
{
type: Sequelize.STRING
}
)
])
}
}

View File

@@ -11,12 +11,12 @@
"dependencies": {
"archiver": "1.3.0",
"async": "2.3.0",
"basic-auth": "1.1.0",
"bcryptjs": "2.4.3",
"body-parser": "^1.19.0",
"cheerio": "0.22.0",
"config": "1.25.1",
"cookie-parser": "~1.4.3",
"ejs": "3.1.5",
"execSync": "latest",
"express": "^4.16.4",
"file-type": "^7.6.0",
@@ -43,7 +43,6 @@
"serve-static": "^1.13.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",

View File

@@ -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) {

View File

@@ -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": "Sinscriure",
"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 despecificar",
"confirm": "Mercés de confirmar",
"error_unknown_email": "Aquesta combinason dadreç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 dopcions.",
"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 dequipa per %s",
"team_invite_membership_body": "Qualquun vos a convidat a %s sus Spacedeck. Mercés de clicar sul ligam seguent per acceptar linvitacion.",
"team_invite_user_body": "Qualquun vos a convidat a %s sus Spacedeck.\nVòstre senhal temporari es « %s».\nMercés de clicar sul ligam seguent per acceptar linvitacion.",
"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 lequipa %s sus Spacedeck",
"space_invite_membership_subject": "Invitacion Espaci per %s: %s",
"space_invite_membership_body": "%s vos a convit a lEspaci « %s»",
"space_invite_membership_action": "Acceptar linvitacion",
"folder_invite_membership_subject": "Espaci",
"folder_invite_membership_body": "Qualquun vos a convidat a Team sus Spacedeck. Clicatz lo ligam seguent per acceptar linvitacion.",
"folder_invite_membership_acction": "Acceptar",
"login_google": "Sidentificar 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 despacis.",
"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: 200x200pixè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 quexplica 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 laccè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 lEspaci",
"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 lescòla e a luniversitat pels mai rics, experiéncia daprendissatge connectat.",
"spaces": "Mos espacis",
"access_editor_link": "Ligam de modificacion dirècta",
"access_editor_link_desc": "Donatz aqueste ligam a qualquun que deu poder modificar dirèctament aqueste Espaci, cap de compte pas requerit: ",
"access_editor_link_desc_slug": "Aqueste ligam conten lo nom de lespaci, tanben. ",
"access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements quan creats.",
"access_current_members": "Membres actuals",
"access_new_members": "Convidar de novèls membres",
"access_no_members": "Los membres daqueste Espacii apreissaràn aquí.",
"comments": "comentaris",
"landing_customers": "La fisança de milièr de personas.",
"landing_features_title": "Un jòc d'enfants dutilizar.",
"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": "<ul><li>Unlimited Spaces</li><li>Exporter PDF, ZIP</li><li>No Watermarks</li><li>Image de fonds</li><li>Activity History</li><li>20 Go de stockage</li><ul>",
"landing_pricing_pro": "€4,90/User/Mo. <br><small> €49,90/User/Year</small>",
"landing_pricing_pro_text": "Avec toute la puissance que vous attendez.",
"landing_pricing_pro_features": "€4,90/User/Mo. <br><small> €49,90/User/Year</small>",
"welcome_subject": "La benvenguda a Spacedeck",
"welcome_body": "Mercés per vòstra inscripcion a Spacedeck.\nEsperam quauretz plaser a trabalhar dins los Espacis. <br> Oblidetz pas que vòstre compte conten un nombre illimitat de collaborators. <br> 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!<br><br>Avètz demandat la reïnicializacion del senhal.<br>Mercés de clicar sul ligam seguent per ne causir un novèl.<br>",
"password_reset_action": "Reïnicializar ara",
"was_offline": "La connexion a Spacedeck es estada copada. Savè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 lequipa",
"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 lespaci",
"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"
}

View File

@@ -233,7 +233,7 @@ var SpacedeckBoardArtifacts = {
},
artifact_thumbnail_uri: function(a) {
if (a.payload_thumbnail_big_uri && a.board) {
if (a.payload_thumbnail_big_uri) {
if (a.w>800) {
return a.payload_thumbnail_big_uri;
}
@@ -360,7 +360,8 @@ var SpacedeckBoardArtifacts = {
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a)}.bind(this));
var max_z = _.max(overlapping,function(a){ return a.z; });
if (max_z.board) {
if (max_z.z) {
max_z = max_z.z + 1;
} else {
max_z = 1;
@@ -378,13 +379,13 @@ var SpacedeckBoardArtifacts = {
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a);}.bind(this));
var min_z = _.min(overlapping,function(a){ return a.z; });
if (min_z.board) {
if (min_z.z) {
min_z = min_z.z - 1;
} else {
min_z = 0;
}
var my_z = _.max(this.selected_artifacts(),function(a){ return a.z; });
if (my_z.board) {
if (my_z.z) {
my_z = my_z.z - 1;
} else {
my_z = 0;

View File

@@ -18,6 +18,21 @@ var SpacedeckRoutes = {
}
]);
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([
{
path: "/confirm/:token",

View File

@@ -197,6 +197,7 @@ var SpacedeckSections = {
Mousetrap.bind('del', function(evt) { this.if_editable(function() {this.delete_selected_artifacts(evt);}) }.bind(this));
Mousetrap.bind('backspace', function(evt) { this.if_editable(function() {this.delete_selected_artifacts(evt);}) }.bind(this));
Mousetrap.bind('esc', function(evt) { this.deselect(); this.deactivate_tool(); }.bind(this));
Mousetrap.bind(['command+d', 'ctrl+d' ], function(evt) { evt.preventDefault(); evt.stopPropagation(); this.if_editable(function() {this.duplicate_selected_artifacts();}) }.bind(this));
Mousetrap.bind(['command+z', 'ctrl+z' ], function(evt) { this.if_editable(function() {this.undo();}) }.bind(this));
Mousetrap.bind(['command+shift+z','ctrl+shift+z'], function(evt) { this.if_editable(function() {this.redo();}) }.bind(this));
@@ -405,7 +406,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() {
@@ -587,8 +593,6 @@ var SpacedeckSections = {
evt.preventDefault();
}
this.active_tool = "pointer";
if (this.opened_dialog == id) {
this.opened_dialog = "none";
return;
@@ -661,14 +665,6 @@ var SpacedeckSections = {
},100);
},
handle_section_keydown: function(evt) {
if (evt.keyCode == 67 && (evt.ctrlKey || evt.metaKey)) { // c key
this.prepare_clipboard();
this.prepare_clipboard_step2();
}
return true;
},
handle_onbeforecopy: function(evt) {
if (this.editing_artifact_id) return;
@@ -792,7 +788,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;
@@ -804,7 +799,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
@@ -870,7 +864,7 @@ var SpacedeckSections = {
if (evt && !evt.shiftKey && this.is_selected(a)) return; // already selected
if (!evt || !evt.shiftKey) {
this.deselect();
this.selected_artifacts_dict = {};
}
if (evt && evt.shiftKey) {
@@ -1131,8 +1125,6 @@ var SpacedeckSections = {
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;
@@ -1610,12 +1602,8 @@ var SpacedeckSections = {
if (this.guest_nickname) {
new_item.editor_name = this.guest_nickname;
}
// console.log("new artifact", new_item);
save_artifact(new_item, function(saved_item) {
// console.log("saved artifact", saved_item);
this.update_board_artifact_viewmodel(saved_item);
this.active_space_artifacts.push(saved_item);
@@ -1723,15 +1711,15 @@ var SpacedeckSections = {
w = 400;
}
//var point = this.find_place_for_item(w,h);
var point = this.cursor_point_to_space(evt);
point.z = this.highest_z()+1;
var a = {
space_id: this.active_space._id,
mime: "x-spacedeck/shape",
description: "",
x: point.x,
y: point.y,
x: point.x+w/2,
y: point.y+h/2,
z: point.z,
w: w,
h: h,
@@ -1861,7 +1849,7 @@ var SpacedeckSections = {
// upload progress
var progress = e.loaded/e.total;
if (progress=1) {
if (progress===1) {
a.description = "Converting Media…";
} else {
a.description = "Upload "+parseInt(progress*100)+"%";
@@ -2553,11 +2541,26 @@ var SpacedeckSections = {
this.toolbar_artifacts_in = false;
},
deactivate_tool: function(evt) {
this.active_tool = "pointer";
},
start_adding_artifact: function(evt) {
evt = fixup_touches(evt);
},
start_adding_note: function(evt) {
this.deselect();
if (this.active_tool == "note") {
this.active_tool = "pointer";
} else {
this.active_tool = "note";
}
this.opened_dialog = "none";
},
start_drawing_scribble: function(evt) {
this.deselect();
if (this.active_tool == "scribble") {
this.active_tool = "pointer";
} else {
@@ -2567,11 +2570,13 @@ var SpacedeckSections = {
},
start_drawing_arrow: function(evt) {
this.deselect();
this.active_tool = "arrow";
this.opened_dialog = "none";
},
start_drawing_line: function(evt) {
this.deselect();
this.active_tool = "line";
this.opened_dialog = "none";
},

View File

@@ -99,11 +99,15 @@ var SpacedeckSpaces = {
}.bind(this), {value: dft || "Guest "+parseInt(10000*Math.random()), ok: __("ok"), cancel: __("cancel")});
},
load_space: function(space_id, on_success, on_error) {
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"));
@@ -610,10 +614,11 @@ var SpacedeckSpaces = {
},
download_space_as_pdf: function(space) {
this.close_dropdown();
this.global_spinner = true;
get_resource("/spaces/" + space._id + "/pdf", function(o) {
this.global_spinner = false;
location.href = o.url;
window.open(o.url, "_blank");
}.bind(this), function(xhr) {
this.global_spinner = false;
alert("PDF export problem (" + xhr.status + ").");

View File

@@ -40,7 +40,11 @@ SpacedeckWebsockets = {
}
} else console.log("artifact created in another space.");
}
else if (msg.action == "update" && msg.object) {
else if ((msg.action == "update" || msg.action == "update-self") && msg.object) {
if (msg.action == "update-self") {
console.log(msg.object);
}
if (this.active_space) {
var o = msg.object;
if (o && o._id) {
@@ -185,7 +189,7 @@ SpacedeckWebsockets = {
return;
}
if (msg.channel_id == channel_id) {
if (msg.channel_id == channel_id && !msg.action.match("-self")) {
return;
}
@@ -199,7 +203,7 @@ SpacedeckWebsockets = {
this.handle_presenter_media_update(msg);
}
if (msg.action == "update" || msg.action == "create" || msg.action == "delete"){
if (msg.action == "update" || msg.action == "update-self" || msg.action == "create" || msg.action == "delete") {
this.handle_live_updates(msg);
}

View File

@@ -100,6 +100,11 @@ function setup_whiteboard_directives() {
return;
}
if ($scope.active_tool == "note") {
this.handle_mouse_down_space(evt, true);
return;
}
var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
if ($scope.active_tool == "eyedrop") {
@@ -213,8 +218,8 @@ function setup_whiteboard_directives() {
$scope.zoom_to_cursor(evt,amount);
},
handle_mouse_down_space: function(evt) {
if (evt.which != 2) {
handle_mouse_down_space: function(evt, force) {
if (!force && evt.which != 2) {
if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
}
@@ -374,12 +379,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;";
@@ -399,15 +402,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
};
},
@@ -524,42 +527,35 @@ 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;
var pt = parseInt($("#space").css("padding-top"));
point.y=(point.y+section_el.scrollTop-pt)/z;
point.x=(point.x+section_el.scrollLeft)/z;
return point;
},
start_drawing_note: function(evt) {
evt.preventDefault();
evt.stopPropagation();
var $scope = this.vm.$root;
var point = this.cursor_point_to_space(evt);
this.offset_point_in_wrapper(point);
var z = $scope.highest_z()+1;
var note_w = 250;
var note_h = 250;
var a = {
space_id: $scope.active_space._id,
mime: "text/html",
description: "<p>Text</p>",
x: point.x,
y: point.y,
x: point.x-note_w/2,
y: point.y-note_h/2,
z: z,
w: 64,
h: 64,
w: note_w,
h: note_h,
align: "center",
valign: "middle",
stroke_color: "#000000",
fill_color: "rgb(241, 196, 15)",
stroke: 0
stroke: 0,
padding_left: 10,
padding_right: 10,
padding_top: 10,
padding_bottom: 10
};
$scope.save_artifact(a, function(saved_a) {
@@ -577,7 +573,7 @@ function setup_whiteboard_directives() {
evt.stopPropagation();
var $scope = this.vm.$root;
var point = this.offset_point_in_wrapper(this.cursor_point_to_space(evt));
var point = this.cursor_point_to_space(evt);
var z = $scope.highest_z()+1;
$scope.deselect();
@@ -592,7 +588,7 @@ function setup_whiteboard_directives() {
z: z,
w: 64,
h: 64,
stroke_color: "#000000",
stroke_color: $scope.active_style.stroke_color,
stroke: 2,
shape: "scribble"
};
@@ -616,7 +612,6 @@ function setup_whiteboard_directives() {
var $scope = this.vm.$root;
var point = this.cursor_point_to_space(evt);
this.offset_point_in_wrapper(point);
var z = $scope.highest_z()+1;
var a = {
@@ -629,7 +624,7 @@ function setup_whiteboard_directives() {
z: z,
w: 64,
h: 64,
stroke_color: "#000000",
stroke_color: $scope.active_style.stroke_color,
stroke: 2,
shape: "arrow"
};
@@ -652,7 +647,6 @@ function setup_whiteboard_directives() {
var $scope = this.vm.$root;
var point = this.cursor_point_to_space(evt);
this.offset_point_in_wrapper(point);
var z = $scope.highest_z()+1;
var a = {
@@ -695,8 +689,7 @@ function setup_whiteboard_directives() {
evt.preventDefault();
if (this.mouse_state == "lasso") {
var lasso_rect = this.abs_rect(this.offset_point_in_wrapper(this.lasso));
// convert to space coordinates
var lasso_rect = this.abs_rect(this.lasso);
if (lasso_rect.w>0 && lasso_rect.h>0) {
var arts = this.artifacts_in_rect(lasso_rect);
@@ -775,18 +768,12 @@ function setup_whiteboard_directives() {
$scope.handle_scroll();
var cursor = this.cursor_point_to_space(evt);
var cursor = this.cursor_point_to_space(evt); // takes the raw event data and finds the mouse location in virtual space
var dx = cursor.x - $scope.mouse_ox;
var dy = cursor.y - $scope.mouse_oy;
var dt = (new Date()).getTime() - this.last_mouse_move_time;
this.last_mouse_move_time = (new Date()).getTime();
var zoom = $scope.viewport_zoom||1;
if (zoom) {
dx/=zoom;
dy/=zoom;
}
// send cursor
if (dx>10 || dy>10 || dt>100) {
var name = "anonymous";
@@ -798,8 +785,8 @@ function setup_whiteboard_directives() {
var cursor_msg = {
action: "cursor",
x: cursor.x/zoom,
y: cursor.y/zoom,
x: cursor.x,
y: cursor.y,
name: name,
id: $scope.user._id||name
};
@@ -969,7 +956,7 @@ function setup_whiteboard_directives() {
var old_a = a;
var control_points = _.cloneDeep(old_a.control_points);
var offset = this.offset_point_in_wrapper({x:cursor.x,y:cursor.y});
var offset = {x:cursor.x,y:cursor.y};
control_points.push({
dx: offset.x-old_a.x,
@@ -989,8 +976,8 @@ function setup_whiteboard_directives() {
if (!$("#space").length) return;
el = $("#space")[0];
el.scrollLeft = this.old_panx - dx*$scope.viewport_zoom;
el.scrollTop = this.old_pany - dy*$scope.viewport_zoom;
el.scrollLeft -= dx*$scope.viewport_zoom;
el.scrollTop -= dy*$scope.viewport_zoom;
$scope.handle_scroll();
}

View File

@@ -121,33 +121,100 @@ function render_vector_drawing(a, padding) {
}
function render_vector_star(edges,xradius,yradius,offset) {
function render_vector_star(tips,width,height,stroke) {
//A 5-pointed (5 tips) regular star of radius from center to tip of 1 has a box around it of width = 2 cos(pi/10) and height = 1 + cos(pi/5)
// assuming the star is oriented with one point directly above the center.
// So the center of the star is at width * 1/2 and height * 0.552786 which is 1 / (1 + cos(pi/5)) (also assuming the y-axis is inverted).
// The inner points are at radius 0.381966 = sin(pi/10)/cos(pi/5).
// Fortunately with simple transformations with matrices, we can do rotations and scales easily.
// See https://en.wikipedia.org/wiki/Rotation_matrix for details.
// But because the stroke is done after scaling (it's not scaled), we have to adjust the points after the rotation and scaling happens.
//A 10-pointed regular star is simpler because it is vertically symmetrical.
edges *= 2;
//NOTE: for very thick stroke widths, and small stars, the star might render very strangely!
var xcenter = width/2;
var ycenter = 0;
var inner_radius = 0;
if (tips == 5) {
ycenter = height * 0.552786;
inner_radius = 0.381966; //scale compared to outer_radius of 1.0
} else {
//tips == 10
ycenter = height/2;
inner_radius = 0.7; //scale compared to outer_radius of 1.0
}
// Coordinates of the first tip, and the first inner corner
var xtip = 1; // radius 1
var ytip = 0;
var xinner = inner_radius * Math.cos(Math.PI/(tips==5?5:10));
var yinner = inner_radius * Math.sin(Math.PI/(tips==5?5:10));
var points = [];
var degrees = 360 / edges;
for (var i=0; i < edges; i++) {
var a = i * degrees - 90;
var xr = xradius;
var yr = yradius;
if (i%2) {
if (edges==20) {
xr/=1.5;
yr/=1.5;
} else {
xr/=2.8;
yr/=2.8;
}
// var tmp_outside_points = []; // uncomment to see the calculated edge of the star (outside the stroke width)
var angle = 2*Math.PI / tips;
// generate points without offset from stroke width first
for (var i=0; i < tips; i++) {
var a = i * angle - Math.PI/2;
// Tip first...
// Rotate the outer tip around the origin:
var x = xtip * Math.cos(a); // because ytip = 0 we don't include: - ytip * Math.sin(a);
var y = xtip * Math.sin(a); // because ytip = 0 we don't include: + ytip * Math.cos(a);
// Scale for the bounding box:
x = x * width / (2 * Math.cos(Math.PI/10));
y = y * height / (tips==5?(1 + Math.cos(Math.PI/5)):2);
points.push([x,y]);
// tmp_outside_points.push(x+" "+y); // uncomment to see the calculated edge of the star (outside the stroke width)
// Now the inner corner...
// Rotate the inner corner around the origin:
x = xinner * Math.cos(a) - yinner * Math.sin(a);
y = xinner * Math.sin(a) + yinner * Math.cos(a);
// Scale for the bounding box:
x = x * width / (2 * Math.cos(Math.PI/10));
y = y * height / (tips==5?(1 + Math.cos(Math.PI/5)):2);
points.push([x,y]);
// tmp_outside_points.push(x+" "+y); // uncomment to see the calculated edge of the star (outside the stroke width)
}
var x = offset + xradius + xr * Math.cos(a * Math.PI / 180);
var y = offset + yradius + yr * Math.sin(a * Math.PI / 180);
points.push(x+","+y);
var inset_points = [];
for (var i=0; i < points.length; i++) {
var pA = points[(((i-1)%points.length)+points.length)%points.length]; // Javascript modulus "bug"
var p0 = points[i];
var pB = points[(i+1)%points.length];
var dAx = p0[0] - pA[0];
var dAy = p0[1] - pA[1];
var dBx = p0[0] - pB[0];
var dBy = p0[1] - pB[1];
var dBLength = Math.sqrt(dBx**2 + dBy**2);
// The trig here is a bit hairy. Basically, finding the inset points is done by finding the angle (theta)
// between the tips and the neighboring inner corners (or vice versa). Then, that angle is used to
// calculate vector scaling factors for half the thickness of the stroked path. Which then is used to find
// the actual inset points for the tips and inner corners.
var theta = Math.atan2(dAx*dBy-dAy*dBx, dAx*dBx + dAy*dBy); // angle between the vectors
var theta = (i%2? Math.PI * 2 - theta : theta);
var stroke_prime = dBLength * Math.tan(theta/2); // this is really a scaling factor
var xprime = p0[0] + (i%2?-1:1)*((stroke/2)/stroke_prime)*dBx + dBy*(stroke/2)/dBLength;
var yprime = p0[1] + (i%2?-1:1)*((stroke/2)/stroke_prime)*dBy + -1 * dBx*(stroke/2)/dBLength;;
inset_points.push(xprime+","+yprime);
}
return "<polygon points='"+points.join(" ")+"'/>";
// NOTE: use svg transformations to center the thing
return "<polygon stroke-miterlimit='64' points='"+inset_points.join(" ")+"' transform='translate(" + xcenter + " " + ycenter + ")'/>";
// Append these if you want to see what is being calculated.
// The cyan dashed line is the outside of the star including the stroke width.
// The red dashed line is just the star polygon points themselves.
// "<polygon stroke-width='4' stroke='red' stroke-dasharray='16 12' fill-opacity='0' points='"+inset_points.join(" ")+"' transform='translate(" + xcenter + " " + ycenter + ")'/>" +
// "<polygon stroke-width='4' stroke='cyan' stroke-dasharray='16 12' fill-opacity='0' points='"+tmp_outside_points.join(" ")+"' transform='translate(" + xcenter + " " + ycenter + ")'/>";
}
function transform_vector_template(cmds, xr, yr, offset) {
@@ -251,8 +318,8 @@ function render_vector_shape(a) {
diamond: function() { return render_vector_ngon(4, xr, yr, offset); },
square: function() { return "" },
triangle: function() { return render_vector_ngon(3, xr, yr, offset); },
star: function() { return render_vector_star(5, xr, yr, offset); },
burst: function() { return render_vector_star(10, xr, yr, offset); },
star: function() { return render_vector_star(5, a.w, a.h, a.stroke); },
burst: function() { return render_vector_star(10, a.w, a.h, a.stroke); },
speechbubble: function() { return render_vector_speechbubble(xr, yr, offset); },
heart: function() { return render_vector_heart(xr, yr, offset); },
cloud: function() { return render_vector_cloud(xr, yr, offset); },

View File

@@ -12651,13 +12651,12 @@ button.close {
position: absolute;
width: 100%;
overflow: hidden;
background-size: cover;
background-color: transparent;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background-position: center 100%;
background-position: left top;
background-repeat: no-repeat; }
#folder-grid .item > a .item-title {
display: block;
@@ -14549,7 +14548,8 @@ button.close {
overflow: scroll; }
.board .wrapper {
background-repeat: no-repeat;
background-size: cover; }
background-size: initial;
background-position: 0 0; }
.snap-ruler-h {
pointer-events: none;
@@ -15350,7 +15350,8 @@ body:not(.present-mode) #space .artifact.selected {
padding: 10px;
background-color: #3d9ee9;
opacity: 0.9;
text-align: center; }
text-align: center;
font-size: 14px; }
.artifact.state-processing .progress-text, .artifact.state-uploading .progress-text {
text-align: center;
padding: 8px;

View File

@@ -114,7 +114,7 @@ router.post('/', function(req, res, next) {
});
router.post('/:artifact_id/payload', function(req, res, next) {
if (req.spaceRole == "editor"  ||  req.spaceRole == "admin") {
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
var a = req.artifact;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
@@ -124,10 +124,21 @@ router.post('/:artifact_id/payload', function(req, res, next) {
var stream = req.pipe(writeStream);
var progressCallback = function(progressMsg) {
// merge progress message with any other changes (size/location)
db.Artifact.findOne({where: {
_id: a._id
}}).then(a => {
if (a) {
a.description = progressMsg.toString();
db.packArtifact(a);
a.save();
redis.sendMessage("update", "Artifact", a, req.channelId);
db.unpackArtifact(a);
redis.sendMessage("update-self", "Artifact", a, req.channelId);
} else {
// artifact has been deleted
// TODO: stop conversion process!
}
});
};
stream.on('finish', function() {
@@ -135,7 +146,8 @@ router.post('/:artifact_id/payload', function(req, res, next) {
if (error) res.status(400).json(error);
else {
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
res.distributeUpdate("Artifact", artifact);
db.unpackArtifact(artifact);
res.distributeUpdate("Artifact", artifact, true);
}
}, progressCallback);
});

View File

@@ -114,7 +114,12 @@ router.get('/pdf', function(req, res, next) {
res.status(201).json({
url: url
});
fs.unlink(local_path);
fs.unlink(local_path, function(){
if (err) console.log('unlink', err);
else {
console.log('unlink', local_path);
}
});
});
}, (err) => {
res.status(500).json({

View File

@@ -42,7 +42,52 @@ var spaceMapping = {
thumbnail_url: 1
};
function listSpacesInFolder(req, res, parent_space_id) {
db.Space
.findOne({where: {
_id: parent_space_id
}})
.then(function(space) {
if (space) {
function spacesForRole(role) {
if (role == "none") {
if (space.access_mode == "public") {
role = "viewer";
}
}
if (role != "none") {
db.Space
.findAll({where:{
parent_space_id: parent_space_id
}, include:[db.CreatorSafeInclude(db)]})
.then(function(spaces) {
res.status(200).json(spaces);
});
} else {
res.status(403).json({"error": "not authorized"});
}
}
if (req["spaceAuth"] && space.edit_hash) {
// TODO could be editor, too
spacesForRole("none");
} else {
db.getUserRoleInSpace(space, req.user, spacesForRole);
}
} else {
res.status(404).json({"error": "space not found"});
}
});
}
router.get('/', function(req, res, next) {
if (req.query.parent_space_id && req["spaceAuth"]) {
// list subspaces of a space authorized anonymously
listSpacesInFolder(req, res, req.query.parent_space_id);
return;
}
if (!req.user) {
res.status(403).json({
error: "auth required"
@@ -83,36 +128,7 @@ router.get('/', function(req, res, next) {
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
// list spaces in a folder
db.Space
.findOne({where: {
_id: req.query.parent_space_id
}})
.then(function(space) {
if (space) {
db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") {
if (space.access_mode == "public") {
role = "viewer";
}
}
if (role != "none") {
db.Space
.findAll({where:{
parent_space_id: req.query.parent_space_id
}, include:[db.CreatorSafeInclude(db)]})
.then(function(spaces) {
res.status(200).json(spaces);
});
} else {
res.status(403).json({"error": "no authorized"});
}
});
} else {
res.status(404).json({"error": "space not found"});
}
});
listSpacesInFolder(req, res, req.query.parent_space_id);
} else {
// list home folder and spaces/folders that the user is a member of
@@ -274,9 +290,13 @@ router.put('/:id', function(req, res) {
db.Space.update(newAttr, {where: {
"_id": space._id
}}).then(rows => {
db.Space.findOne({ where: {
"_id": space._id
}}).then(space => {
res.distributeUpdate("Space", space);
});
});
});
router.post('/:id/background', function(req, res, next) {
@@ -294,7 +314,7 @@ router.post('/:id/background', function(req, res, next) {
if (space.background_uri) {
var oldPath = url.parse(req.space.background_uri).pathname;
uploader.removeFile(oldPath, function(err) {
console.error("removed old bg error:", err);
console.error("remove old background error:", err);
});
}
@@ -302,13 +322,18 @@ router.post('/:id/background', function(req, res, next) {
background_uri: backgroundUrl
}, {
where: { "_id": space._id }
}, function(rows) {
}).then(rows => {
fs.unlink(localFilePath, function(err) {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(200).json(space);
db.Space.findOne({ where: {
"_id": space._id
}}).then(space => {
console.log("========== space update:", space);
res.distributeUpdate("Space", space);
});
}
});
});

View File

@@ -11,7 +11,6 @@ var importer = require('../../helpers/importer');
var bcrypt = require('bcryptjs');
var crypto = require('crypto');
var swig = require('swig');
var async = require('async');
var _ = require('underscore');
var fs = require('fs');

View File

@@ -15,7 +15,7 @@ const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
router.get('/', (req, res) => {
res.render('index', { title: 'Spaces' });
res.render('index', { config:config, user:req.user });
});
router.get('/ping', (req, res) => {
@@ -23,35 +23,35 @@ router.get('/ping', (req, res) => {
});
router.get('/spaces', (req, res) => {
res.render('spacedeck', { title: 'Spaces' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/not_found', (req, res) => {
res.render('not_found', { title: 'Spaces' });
res.render('not_found', {});
});
router.get('/confirm/:token', (req, res) => {
res.render('spacedeck', { title: 'Space' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/folders/:id', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/signup', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/accept/:id', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/password-reset', (req, res) => {
res.render('spacedeck', { title: 'Signup' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/password-confirm/:token', (req, res) => {
res.render('spacedeck', { title: 'Signup' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/de/*', (req, res) => {
@@ -70,6 +70,14 @@ router.get('/fr', (req, res) => {
res.redirect("/t/fr");
});
router.get('/oc/*', (req, res) => {
res.redirect("/t/oc");
});
router.get('/oc', (req, res) => {
res.redirect("/t/oc");
});
router.get('/en/*', (req, res) => {
res.redirect("/t/en");
});
@@ -83,27 +91,11 @@ router.get('/account', (req, res) => {
});
router.get('/login', (req, res) => {
res.render('spacedeck');
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/logout', (req, res) => {
res.render('spacedeck');
});
router.get('/contact', (req, res) => {
res.render('public/contact');
});
router.get('/about', (req, res) => {
res.render('public/about');
});
router.get('/terms', (req, res) => {
res.render('public/terms');
});
router.get('/privacy', (req, res) => {
res.render('public/privacy');
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/t/:id', (req, res) => {
@@ -115,22 +107,22 @@ router.get('/t/:id', (req, res) => {
res.redirect(path);
});
router.get('/s/:token', (req, res) => {
var token = req.params.token;
if (token.split("-").length > 0) {
token = token.split("-")[0];
router.get('/s/:hash', (req, res) => {
var hash = req.params.hash;
if (hash.split("-").length > 0) {
hash = hash.split("-")[0];
}
db.Space.findOne({where: {"edit_hash": token}}).then(function (space) {
db.Space.findOne({where: {"edit_hash": hash}}).then(function (space) {
if (space) {
if (req.accepts('text/html')){
res.redirect("/spaces/"+space._id + "?spaceAuth=" + token);
res.redirect("/spaces/"+space._id + "?spaceAuth=" + hash);
} else {
res.status(200).json(space);
}
} else {
if (req.accepts('text/html')) {
res.status(404).render('not_found', { title: 'Page Not Found.' });
res.status(404).render('not_found', {});
} else {
res.status(404).json({});
}
@@ -139,7 +131,7 @@ router.get('/s/:token', (req, res) => {
});
router.get('/spaces/:id', (req, res) => {
res.render('spacedeck', { title: 'Space' });
res.render('spacedeck', { config:config, user:req.user });
});
module.exports = router;

View File

@@ -16,7 +16,6 @@ const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const swig = require('swig');
const i18n = require('i18n-2');
const helmet = require('helmet');
@@ -34,22 +33,13 @@ console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev'));
i18n.expressBind(app, {
locales: ["en", "de", "fr"],
locales: ["en", "de", "fr", "oc", "es"],
defaultLocale: "en",
cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development')
});
swig.setDefaults({
varControls: ["[[", "]]"] // otherwise it's not compatible with vue.js
});
swig.setFilter('cdn', function(input, idx) {
return input;
});
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('view engine', 'ejs');
if (isProduction) {
app.set('views', path.join(__dirname, 'build', 'views'));
@@ -114,7 +104,6 @@ if (config.get('storage_local_path')) {
//app.use(require('./middlewares/404'));
if (app.get('env') == 'development') {
app.set('view cache', false);
swig.setDefaults({cache: false});
} else {
app.use(require('./middlewares/500'));
}
@@ -125,9 +114,10 @@ module.exports = app;
db.init();
// START WEBSERVER
const port = 9666;
const host = config.get('host');
const port = config.get('port');
const server = http.Server(app).listen(port, () => {
const server = http.Server(app).listen(port, host, () => {
if ("send" in process) {
process.send('online');

View File

@@ -570,6 +570,7 @@ body:not(.present-mode) {
background-color: $blue;
opacity: 0.9;
text-align: center;
font-size: 14px;
}
.progress-text {
text-align: center;

View File

@@ -419,14 +419,13 @@
width: 100%;
overflow: hidden;
background-size: cover;
background-color: transparent;
border-top-left-radius: $radius*2;
border-top-right-radius: $radius*2;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background-position: center 100%;
background-position: left top;
background-repeat: no-repeat;
}

View File

@@ -119,7 +119,8 @@
.wrapper {
background-repeat: no-repeat;
background-size: cover;
background-size: initial;
background-position: 0 0;
}
width: 100%;

View File

@@ -1,24 +0,0 @@
<html>
<body>
<h2>[[space.name]]</h2>
<table class="table table-striped" border=1>
<tr>
<th>created</th>
<th>updated</th>
<th>filetype</th>
<th>filename</th>
<th>preview</th>
</tr>
{% for a in space.artifacts %}
<tr>
<td>[[ a.created_at | date('d.m.Y H:i') ]] by [[ a.user.email ]][[ a.editor_name ]]</td>
<td>[[ a.updated_at | date('d.m.Y H:i') ]] by [[ a.update_user.email ]][[ a.last_update_editor_name ]]</td>
<td>[[ a.mime ]]</td>
<td>{% if a.payload_uri %}<a href="[[a.payload_uri]]">[[ a.filename ]]</a>{% endif %}</td>
<td>[[ a.description ]]</td>
</tr>
{% endfor %}
</table>
</body>
</html>

View File

@@ -1,11 +0,0 @@
[[ text | safe ]]
{% if options.message %}
<p>
<i>[[options.message]]</i>
</p>
{% endif %}
{% if options.action %}<br><br>
<a href="[[options.action.link]]" target="_blank">[[options.action.name]]</a><br>
{% endif %}

5
views/error.ejs Normal file
View File

@@ -0,0 +1,5 @@
<%- include('layouts/outer-header') -%>
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>
<%- include('layouts/outer-footer') -%>

View File

@@ -1,3 +0,0 @@
<h1>[[ message ]]</h1>
<h2>[[ error.status ]]</h2>
<pre>[[ error.stack ]]</pre>

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>[[space.name]]</title>
<meta charset="utf-8" />
<meta property="og:title" content="[[space.name]]" />
<meta property="og:description" content="" />
<meta property="og:updated_time" content="[[space.updated_at.getTime()]]" />
<meta property="og:type" content="website" />
<meta property="og:image" content="[[space.thumbnail_url]]" />
</head>
<body>
<h1>[[space.name]]</h1>
{% for a in space.artifacts %}
<tr>
<td>[[ a.mime ]]</td>
<td>[[ a.description | striptags ]]</td>
<td>{% if a.payload_uri %}<a href="[[ a.payload_uri ]]">download</a>{% endif %}</td>
</tr>
{% endfor %}
</body>
</html>

View File

@@ -1,9 +1,4 @@
{% extends 'layouts/outer.html' %}
{% block title %}Spacedeck{% endblock %}
{% block content %}
<%- include('layouts/outer-header') %>
<div id="landing">
<section>
<h1>Work Together, Visually.</h1>
@@ -30,5 +25,4 @@
</p>
</section>
</div>
{% endblock %}
<%- include('layouts/outer-footer') %>

View File

@@ -0,0 +1,14 @@
<div class="footer">
<p>
<div class="col-xs-6">
&copy; 2020 <a href="https://mntre.com">MNT Research GmbH</a>, Fehlerstr. 8, 12161 Berlin, Germany<br>
&copy; 20112020 Spacedeck GmbH (in liquidation)<br>
Source Code: <a href="https://github.com/mntmn/spacedeck-open">https://github.com/mntmn/spacedeck-open</a>
<br>
Font: <a href="https://rsms.me/inter/">Inter by rsms</a>
</div>
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,29 @@
<!doctype html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>Spacedeck Open</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link href="/images/favicon.png" rel="icon" type="image/x-icon" />
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<header id="landing-header" class="header">
<div class="header-left">
<a class="btn btn-transparent btn-nude" href="<%= config.endpoint %>/"><img src="/images/sd6-logo-black.svg" width="190"></a>
</div>
<div class="header-right pull-right">
<% if (!user) { %>
<a class="btn btn-md btn-dark btn-round" href="/login"><%=__("login")%></a>
<a class="btn btn-md btn-dark btn-round" href="/signup"><%=__("signup")%></a>
<% } else { %>
<a class="btn btn-md btn-dark btn-round" href="/spaces"><%=__("spaces")%></a>
<a class="btn btn-md btn-dark btn-round" href="/logout"><%=__("logout")%></a>
<% } %>
</div>
</header>

View File

@@ -1,50 +0,0 @@
<!doctype html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>Spacedeck Open {% block title %}{% endblock %}</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" />
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
<script> var csrf_token = '[[ csrf_token ]]'; </script>
<!--script src="[[ '/javascripts/jquery-2.1.4.min.js' | cdn ]]"></script-->
</head>
<body>
<header id="landing-header" class="header">
<div class="header-left">
<a class="btn btn-transparent btn-nude" href="[[config.endpoint]]/"><img src="[[ '/images/sd6-logo-black.svg' | cdn ]]" width="190"></a>
</div>
<div class="header-right pull-right">
{% if !user %}
<a class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a>
<a class="btn btn-md btn-dark btn-round" href="/signup">[[__("signup")]]</a>
{% else %}
<a class="btn btn-md btn-dark btn-round" href="/spaces">[[__("spaces")]]</a>
<a class="btn btn-md btn-dark btn-round" href="/logout">[[__("logout")]]</a>
{% endif %}
</div>
</header>
{% block content %}{% endblock %}
<div class="footer">
<p>
<div class="col-xs-6">
&copy; 2020 <a href="https://mntre.com">MNT Research GmbH</a>, Fehlerstr. 8, 12161 Berlin, Germany<br>
&copy; 20112019 Spacedeck GmbH (in liquidation)<br>
Source Code: <a href="https://github.com/mntmn/spacedeck-open">https://github.com/mntmn/spacedeck-open</a>
<br>
Font: <a href="https://rsms.me/inter/">Inter by rsms</a>
</div>
</p>
</div>
</body>
</html>

4
views/not_found.ejs Normal file
View File

@@ -0,0 +1,4 @@
<div id="landing" style="padding-top:200px;margin:auto;width:300px;">
<h1><%=__("not_found")%></h1>
</div>

View File

@@ -1,11 +0,0 @@
{% extends 'layouts/outer.html' %}
{% block title %}[[ __("not_found") ]]{% endblock %}
{% block content %}
<div id="landing" style="padding-top:200px;margin:auto;width:300px;">
<h1>[[__("not_found")]]</h1>
</div>
{% endblock %}

View File

@@ -17,11 +17,11 @@
<div class="dialog-freestanding dialog in" v-if="active_view == 'account' && user" v-cloak>
<div class="dialog-tabs" style="margin:auto">
<div class="dialog-tab" v-bind:class="{open:account=='profile'}" v-on:click="account='profile'"><span>[[__("profile_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='language'}" v-on:click="account='language'"><span>[[__("language_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='notifications'}" v-on:click="account='notifications'"><span>[[__("notifications_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='password'}" v-on:click="account='password'"><span>[[__("password_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='terminate'}" v-on:click="account='terminate'"><span>[[__("terminate_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='profile'}" v-on:click="account='profile'"><span><%=__("profile_caption")%></span></div>
<div class="dialog-tab" v-bind:class="{open:account=='language'}" v-on:click="account='language'"><span><%=__("language_caption")%></span></div>
<div class="dialog-tab" v-bind:class="{open:account=='notifications'}" v-on:click="account='notifications'"><span><%=__("notifications_caption")%></span></div>
<div class="dialog-tab" v-bind:class="{open:account=='password'}" v-on:click="account='password'"><span><%=__("password_caption")%></span></div>
<div class="dialog-tab" v-bind:class="{open:account=='terminate'}" v-on:click="account='terminate'"><span><%=__("terminate_caption")%></span></div>
</div>
<div class="dialog-section text-left">
@@ -44,11 +44,11 @@
<div class="form-group">
<label class="file btn btn-md btn-darken" style="margin-right: 5px;">
<input type="file" v-on:change="save_user_avatar_image(this)">
<span v-if="!uploading_avatar">[[__("upload_avatar")]]</span>
<span v-if="uploading_avatar">[[__("uploading_avatar")]]</span>
<span v-if="!uploading_avatar"><%=__("upload_avatar")%></span>
<span v-if="uploading_avatar"><%=__("uploading_avatar")%></span>
</label>
<p class="message">[[__("avatar_dimensions")]]</p>
<p class="message"><%=__("avatar_dimensions")%></p>
</div>
</div>
</div>
@@ -66,7 +66,7 @@
</div>
<div class="form-group">
<label class="label" >[[__("profile_name")]]</label>
<label class="label" ><%=__("profile_name")%></label>
<input type="text" id="user-nickname"
pattern=".{3,}"
required title="3 characters minimum"
@@ -75,7 +75,7 @@
</div>
<div class="form-group">
<label class="label">[[__("profile_email")]]</label>
<label class="label"><%=__("profile_email")%></label>
<input
type="email"
id="new-email"
@@ -105,6 +105,12 @@
<label class="radio" v-bind:class="{checked: user.prefs_language=='fr'}" v-on:click="save_user_language('fr')">
<input type="radio" id="user-preferences_language" name="language" value="fr"><span>Français</span>
</label>
<label class="radio" v-bind:class="{checked: user.prefs_language=='oc'}" v-on:click="save_user_language('oc')">
<input type="radio" id="user-preferences_language" name="language" value="oc"><span>Occitan</span>
</label>
<label class="radio" v-bind:class="{checked: user.prefs_language=='es'}" v-on:click="save_user_language('es')">
<input type="radio" id="user-preferences_language" name="language" value="es"><span>Español</span>
</label>
</div>
</div>
@@ -114,7 +120,7 @@
<label class="checkbox"
v-bind:class="{checked: user.prefs_email_notifications}"
v-on:click="account_save_user_notifications(!user.prefs_email_notifications);">
<span>[[__('notifications_option_chat')]]</span>
<span><%=__('notifications_option_chat')%></span>
</label>
</div>
</div>
@@ -124,15 +130,15 @@
<h4>Change Password</h4>
<div class="modal-section labels-inline">
<div class="form-group">
<label class="label">[[__("current_password")]]</label>
<label class="label"><%=__("current_password")%></label>
<input id="current-password" class="input input-white no-b" v-model="password_change_current" type="password">
</div>
<div class="form-group">
<label class="label">[[__("new_password")]]</label>
<label class="label"><%=__("new_password")%></label>
<input id="new-password" class="input input-white no-b" v-model="password_change_new" type="password">
</div>
<div class="form-group">
<label class="label">[[__("verify_password")]]</label>
<label class="label"><%=__("verify_password")%></label>
<input id="new-password-confirmation" class="input input-white no-b" v-model="password_change_new_confirmation" type="password">
</div>
@@ -143,7 +149,7 @@
<button
class="btn btn-dark btn-md"
v-on:click="save_user_password(password_change_current, password_change_new, password_change_new_confirmation);" >
[[__("change_password")]]
<%=__("change_password")%>
</button>
</div>
</div>
@@ -152,14 +158,14 @@
<h4>Terminate Account</h4>
<div class="modal-section labels-inline">
<div class="form-group">
<label class="label">[[__("current_password")]]</label>
<label class="label"><%=__("current_password")%></label>
<input v-model="account_remove_password" class="input input-white no-b" type="password">
</div>
<div class="form-group">
<label class="label">[[__("terminate_reason")]]</label>
<label class="label"><%=__("terminate_reason")%></label>
<textarea class="input input-white no-b" v-model="account_remove_feedback"></textarea>
<p class="message">[[__("terminate_reason_caption")]]</p>
<p class="message"><%=__("terminate_reason_caption")%></p>
</div>
</div>

View File

@@ -3,43 +3,43 @@
<a class="btn btn-dark btn-md btn-round btn-icon" href="/spaces">
<span class="icon icon-svg icon-sd6"></span>
</a>
<button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-dark btn-md btn-round" v-on:click="create_space('space')">[[ __('create_space') ]]</button>
<button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-dark btn-md btn-round" v-on:click="create_space('space')"><%= __('create_space') %></button>
<button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-stroke-darken btn-md btn-round" v-on:click="create_space('folder')">
<span>[[ __('create_folder') ]]</span>
<span><%= __('create_folder') %></span>
</button>
<label class="relative compact-hidden">
<label class="relative compact-hidden" v-if="logged_in">
<span class="icon icon-sm icon-zoom no-events absolute-top-left" style="margin: 5px;"></span>
<input id="folder-search"
type="search" name="search"
style="padding-left: 40px !important; margin-right: 10px;"
placeholder="[[ __('search') ]]"
placeholder="<%= __('search') %>"
class="input input-md input-white input-round no-b w-2"
v-model="folder_spaces_search" v-on:change="search_spaces">
</label>
<div class="dropdown top light m-r-20 compact-hidden" v-bind:class="{open : active_dropdown=='folder_sorting'}">
<div class="dropdown top light m-r-20 compact-hidden" v-bind:class="{open : active_dropdown=='folder_sorting'}" v-if="logged_in">
<button class="btn btn-sm btn-nude" v-on:click="activate_dropdown('folder_sorting')">
<span>[[ __('sort_by') ]]</span>:
<b v-if="folder_sorting=='updated_at'">[[ __('last_modified') ]]</b>
<b v-if="folder_sorting=='name'">[[ __('title') ]]</b>
<b v-if="folder_sorting=='space_type'">[[ __('type') ]]</b>
<span><%= __('sort_by') %></span>:
<b v-if="folder_sorting=='updated_at'"><%= __('last_modified') %></b>
<b v-if="folder_sorting=='name'"><%= __('title') %></b>
<b v-if="folder_sorting=='space_type'"><%= __('type') %></b>
</button>
<div class="dropdown-menu" role="menu">
<ul class="select-list">
<li v-bind:class="{checked:folder_sorting=='updated_at'}"
v-on:click="set_folder_sorting('updated_at',true)">
<span>[[ __('last_modified') ]]</span>
<span><%= __('last_modified') %></span>
</li>
<li v-bind:class="{checked:folder_sorting=='name'}"
v-on:click="set_folder_sorting('name',false)">
<span>[[ __('title') ]]</span>
<span><%= __('title') %></span>
</li>
<li v-bind:class="{checked:folder_sorting=='space_type'}"
v-on:click="set_folder_sorting('space_type',false)">
<span>[[ __('type') ]]</span>
<span><%= __('type') %></span>
</li>
</ul>
</div>
@@ -50,6 +50,7 @@
<div class="dropdown top right light" v-bind:class="{open: active_dropdown=='account'}">
<button
class="profile-avatar btn btn-md btn-icon btn-dark btn-round"
v-if="logged_in"
v-bind:style="background_image_style([user.avatar_thumb_uri])"
v-bind:class="{'has-avatar-image':!!user.avatar_thumb_uri}" v-on:click="show_account();">
<span class="icon icon-user" v-if="logged_in && !user.avatar_thumb_uri"></span></button>
@@ -59,35 +60,27 @@
<li v-if="user.team && is_admin(user)">
<a href="/team">
<span class="icon icon-sm icon-user-group"></span>
<span>[[ __('edit_team') ]]</span>
<span><%= __('edit_team') %></span>
</a>
</li>
<li>
<a href="/account">
<span class="icon icon-sm icon-user"></span>
<span>[[ __('edit_account') ]]</span>
<span><%= __('edit_account') %></span>
</a>
</li>
<li v-on:click="logout()">
<span>
<span class="icon icon-sm icon-logout"></span>
<span>[[ __('log_out') ]]</span>
<span><%= __('log_out') %></span>
</span>
</li>
</ul>
</div>
</div>
<!--div class="btn-group dark round" id="meta-toggle" style="margin-right:10px">
<button class="btn btn-md btn-transparent btn-icon btn-icon" v-on:click="toggle_meta()">
<span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span>
<span class="icon icon-menu"></span>
</button>
</div-->
</div>
</header>
@@ -98,41 +91,41 @@
<div id="folder-breadcrumb">
<span v-for="item in active_space_path" class="btn btn-sm btn-transparent" v-sd-droppable="handle_folder_drop;item">
<span v-if="logged_in" v-for="item in active_space_path" class="btn btn-sm btn-transparent" v-sd-droppable="handle_folder_drop;item">
<a href="/{{item.space_type}}s/{{item._id}}">{{item.name}}</a>&nbsp;</span>
<a v-if="(active_space_role != 'admin')" type="button" class="btn btn-sm btn-transparent">
<span>{{active_folder.name}}</span>
</a>
<div class="dropdown top light" v-bind:class="{open:active_dropdown=='breadcrumb'}" v-if="(active_folder._id != user.home_folder_id) && ((active_space_role == 'admin') || (active_space_role == 'editor'))">
<div class="dropdown top light" v-bind:class="{open:active_dropdown=='breadcrumb'}" v-if="(active_folder._id != user.home_folder_id) && ((active_space_role == 'admin'))">
<button type="button" class="btn btn-sm btn-transparent btn-dropdown" data-toggle="dropdown" v-on:click=" activate_dropdown('breadcrumb')">
<span>{{active_folder.name}}</span>
</button>
<div class="dropdown-menu" v-if="active_folder && active_folder._id != user.home_folder_id">
<ul class="select-list">
<li><span class="tile-rename" v-on:click="rename_folder(active_folder)">[[__("rename")]]</span></li>
<li v-if="active_space_role == 'admin'"><span class="tile-share" v-on:click="activate_access()">[[__("share")]]</span></li>
<li><span class="tile-rename" v-on:click="rename_folder(active_folder)"><%=__("rename")%></span></li>
<li v-if="active_space_role == 'admin'"><span class="tile-share" v-on:click="activate_access()"><%=__("share")%></span></li>
</ul>
</div>
</div>
<div v-if="active_folder._id == user.home_folder_id">
<span>[[ __('home') ]]</span>
<span><%= __('home') %></span>
</div>
</div>
<div id="folder-empty" v-if="(active_profile_spaces.length == 0)">
<div>
<p>[[ __('no_spaces_yet') ]]</p>
<p><%= __('no_spaces_yet') %></p>
</div>
</div>
<div id="folder-empty" v-if="folder_spaces_filter">
<div v-if="active_profile_spaces | empty?">
<p><b>"{{folder_spaces_filter}}"</b> <br/>[[ __('search_no_results') ]]</p>
<button type="button" class="btn btn-md btn-round btn-stroke-darken events" v-on:click=" folder_spaces_filter = ''">[[ __('search_clear') ]]</button>
<p><b>"{{folder_spaces_filter}}"</b> <br/><%= __('search_no_results') %></p>
<button type="button" class="btn btn-md btn-round btn-stroke-darken events" v-on:click=" folder_spaces_filter = ''"><%= __('search_clear') %></button>
</div>
</div>
<div id="folder-grid">
@@ -143,7 +136,14 @@
draggable="true"
class="item" v-bind:class="item.space_type"
v-bind:style="{'z-index': (active_profile_spaces.length - $index)}">
<a href="/{{item.space_type}}s/{{item._id}}">
<!-- anonymous editors can go edit spaces in a folder -->
<a href="/s/{{item.edit_hash}}-{{item.edit_slug}}" v-if="active_space_role=='editor' && !logged_in">
<span class="item-thumbnail thumbnail-loading" v-if="item.space_type=='space'"></span>
<span class="item-thumbnail" v-bind:style="space_thumbnail_style(item)"></span>
</a>
<a v-if="active_space_role=='viewer' || logged_in" href="/{{item.space_type}}s/{{item._id}}">
<span class="item-thumbnail thumbnail-loading" v-if="item.space_type=='space'"></span>
<span class="item-thumbnail" v-bind:style="space_thumbnail_style(item)"></span>
</a>
@@ -156,8 +156,9 @@
<div class="dropdown-menu" role="menu">
<ul class="select-list">
<li v-on:click="rename_space(item)"><span><span class="icon icon-sm icon-tag"></span>[[ __('rename') ]]</span></li>
<li v-on:click="delete_space(item)"><span><span class="icon icon-sm icon-trash"></span>[[ __('delete') ]]</span></li>
<li v-on:click="download_space_as_pdf(item)"><span><span class="icon icon-sm icon-clipboard"></span><%= __('download_as_pdf') %></span></li>
<li v-on:click="rename_space(item)"><span><span class="icon icon-sm icon-tag"></span><%= __('rename') %></span></li>
<li v-on:click="delete_space(item)"><span><span class="icon icon-sm icon-trash"></span><%= __('delete') %></span></li>
</ul>
</div>
</div>

View File

@@ -4,8 +4,8 @@
</div>
<div class="header-right pull-right">
<a v-if="active_view != 'login'" class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a>
<a v-if="active_view != 'signup'" class="btn btn-md btn-dark btn-round" href="/signup">[[__("signup")]]</a>
<a v-if="active_view != 'login'" class="btn btn-md btn-dark btn-round" href="/login"><%= __("login") %></a>
<a v-if="active_view != 'signup'" class="btn btn-md btn-dark btn-round" href="/signup"><%= __("signup") %></a>
</div>
</header>
@@ -21,10 +21,10 @@
<div class="tight">
<div class="form-group">
<input class="input" name="email" type="email" required v-model="user_forms_email" placeholder="[[__("email")]]">
<input class="input" name="email" type="email" required v-model="user_forms_email" placeholder="<%=__("email")%>">
</div>
<div class="form-group">
<input class="input" name="password" type="password" required v-model="login_password" placeholder="[[__("password")]]">
<input class="input" name="password" type="password" required v-model="login_password" placeholder="<%=__("password")%>">
</div>
</div>
@@ -45,15 +45,15 @@
<div id="signup" v-bind:class="{active : active_view == 'signup'}">
<div class="content">
<form v-on:submit="signup_submit($event, user_forms_name, user_forms_email, signup_password, signup_password_confirmation, signup_invite_code)">
<h4>[[__("signup")]]</h4>
<h4><%=__("signup")%></h4>
<div class="tight">
<div class="form-group">
<input class="input" type="email" required id="user-email" v-model="user_forms_email" placeholder="[[__("email")]]" autofocus v-focus>
<input class="input" type="email" required id="user-email" v-model="user_forms_email" placeholder="<%=__("email")%>" autofocus v-focus>
</div>
<div class="form-group">
<input class="input" id="user-password" required type="password" v-model="signup_password" placeholder="[[__("password")]]">
<input class="input" id="user-password" required type="password" v-model="signup_password" placeholder="<%=__("password")%>">
</div>
<div class="form-group">
@@ -74,8 +74,8 @@
</div-->
<button class="btn btn-dark btn-block">
<span v-if="!creating_user">[[__("signup")]]</span>
<span v-if="creating_user">[[__("signing_up")]]</span>
<span v-if="!creating_user"><%=__("signup")%></span>
<span v-if="creating_user"><%=__("signing_up")%></span>
</button>
<div class="center alert alert-danger" style="width:100%;" v-if="signup_error">{{signup_error}}</div>
@@ -90,16 +90,16 @@
<h4>Password Recovery</h4>
<div class="tight">
<div class="form-group">
<input class="input" type="email" id="user-email" v-model="reset_email" placeholder="[[__("email")]]">
<input class="input" type="email" id="user-email" v-model="reset_email" placeholder="<%=__("email")%>">
</div>
</div>
<div class="text-center alert alert-danger" v-if="password_reset_error">{{password_reset_error}}</div>
<button class="btn btn-dark btn-block" v-on:click="password_reset_submit($event, reset_email)">[[__("reset_password")]]</button>
<button class="btn btn-dark btn-block" v-on:click="password_reset_submit($event, reset_email)"><%=__("reset_password")%></button>
</form>
</div>
<div class="content" v-if="password_reset_send==true">
<h4>Reset Password</h4>
Please check your email inbox.
<h4><%=__("reset_password")%></h4>
<%=__("password_check_inbox")%>
</div>
</div>
@@ -119,7 +119,7 @@
</div>
<div class="text-center alert alert-danger" v-if="password_reset_confirm_error">{{password_reset_confirm_error}}</div>
<button class="btn btn-dark btn-block" v-on:click="password_reset_confirm($event, signup_password, signup_password_confirmation)">[[__("save")]]</button>
<button class="btn btn-dark btn-block" v-on:click="password_reset_confirm($event, signup_password, signup_password_confirmation)"><%=__("save")%></button>
</form>
</div>
</div>

View File

@@ -9,23 +9,23 @@
<div style="margin-bottom: 15px; margin-top: 8px" >
<small>
[[__("created_by")]] <b>{{active_folder.creator.nickname||active_folder.creator.slug}}.</b>
<br/>[[__("last_updated")]] <b>{{active_folder.updated_at | date 'MMMM Do YYYY, HH:mm'}}.</b>
<%=__("created_by")%> <b>{{active_folder.creator.nickname||active_folder.creator.slug}}.</b>
<br/><%=__("last_updated")%> <b>{{active_folder.updated_at | date 'MMMM Do YYYY, HH:mm'}}.</b>
</small>
</div>
<div v-if="logged_in && user.home_folder_id!=active_folder._id">
<button class="btn btn-sm btn-round btn-primary m-r-5" v-on:click="activate_access()">
<span class="icon-label">[[__("share")]]</span>
<span class="icon-label"><%=__("share")%></span>
</button>
</div>
</div>
<div class="sidebar-section" v-if="is_pro(user)">
<h5>[[__("history_recently_updated")]]</h5>
<h5><%=__("history_recently_updated")%></h5>
<div v-if="active_folder_history_items.length == 0">
[[__("history_recently_empty")]]
<%=__("history_recently_empty")%>
</div>
<ul id="updates">
@@ -33,7 +33,7 @@
<a v-bind:href="'/spaces/' + item.space._id">{{item.space.name}}</a>
<small>
[[__("by")]]
<%=__("by")%>
<span v-for="u in item.users">{{u}}<span v-if="$index < (item.users.length-1)">, </span></span>
</small>
</li>
@@ -41,10 +41,10 @@
</div>
<div class="sidebar-section" v-if="!is_pro(user)">
<h5>[[__("history_recently_updated")]]</h5>
<h5><%=__("history_recently_updated")%></h5>
<p>
[[__("pro_ad_history_headline")]]
<%=__("pro_ad_history_headline")%>
</p>
<p>

View File

@@ -9,8 +9,8 @@
<div style="margin-bottom: 15px; margin-top: 8px" >
<small>
[[__("created_by")]] <b>{{active_space.creator.nickname}}.</b><br/>
[[__("last_updated")]] <b>{{active_space.updated_at | date 'MMMM Do YYYY, HH:mm'}}.</b>
<%=__("created_by")%> <b>{{active_space.creator.nickname}}.</b><br/>
<%=__("last_updated")%> <b>{{active_space.updated_at | date 'MMMM Do YYYY, HH:mm'}}.</b>
</small>
</div-->
@@ -21,18 +21,18 @@
</button>
<button class="btn btn-sm btn-round btn-primary m-r-5"
v-on:click="guest_logout()">
[[__("logout")]]
<%=__("logout")%>
</button>
</div>
</div>
<div class="sidebar-section">
<h5>[[__("chat")]] &nbsp; <a v-if="active_space_role!='viewer'" v-on:click="activate_access()" class="btn btn-xs btn-darken">Add People</a></h5>
<h5><%=__("chat")%> &nbsp; <a v-if="active_space_role!='viewer'" v-on:click="activate_access()" class="btn btn-xs btn-darken">Add People</a></h5>
<textarea id="new-comment" style="min-height:80px;padding: 5px 9px;margin-bottom:10px" class="input input-darken no-b" v-if="can_add_comment" v-model="space_comment" placeholder="[[__("chat_message_placeholder")]]" spellcheck="false"></textarea>
<textarea id="new-comment" style="min-height:80px;padding: 5px 9px;margin-bottom:10px" class="input input-darken no-b" v-if="can_add_comment" v-model="space_comment" placeholder="<%=__("chat_message_placeholder")%>" spellcheck="false"></textarea>
<div v-if="can_add_comment">
<button class="btn btn-sm btn-primary" v-on:click="create_space_comment(space_comment)">[[__("post")]]</button>
<button class="btn btn-sm btn-primary" v-on:click="create_space_comment(space_comment)"><%=__("post")%></button>
</div>
<ul class="comments">
@@ -50,7 +50,7 @@
class="delete pull-right"
v-if="active_space_role!='viewer'"
v-on:click="remove_space_comment(item)">
<a title="[[__("delete")]]" style="cursor:pointer"></a>
<a title="<%=__("delete")%>" style="cursor:pointer"></a>
</li>
</ul>

View File

@@ -3,7 +3,7 @@
<div class="modal-dialog">
<div class="modal-content" style="width:760px">
<div class="modal-header" style="padding-bottom:0">
<h3 class="text-left">[[__("share")]]: {{access_settings_space.name}}</h3>
<h3 class="text-left"><%=__("share")%>: {{access_settings_space.name}}</h3>
<button type="button" class="btn btn-icon btn-light btn-round close" v-on:click=" close_modal()">
<span class="icon icon-cross-1"></span>
</button>
@@ -12,17 +12,17 @@
<div class="modal-body">
<div class="modal-section" style="padding-top:0;padding-bottom:20px">
<label class="radio" v-bind:class="{checked:access_settings_space.access_mode=='private'}">
<input type="radio" name="access_mode" value="private" v-model="access_settings_space.access_mode" v-on:click="save_space_access_mode($event)"> [[__("mode_private")]]
<input type="radio" name="access_mode" value="private" v-model="access_settings_space.access_mode" v-on:click="save_space_access_mode($event)"> <%=__("mode_private")%>
</label>
<label class="radio" v-bind:class="{checked:access_settings_space.access_mode=='public'}">
<input type="radio" name="access_mode" value="public" v-model="access_settings_space.access_mode" v-on:click="save_space_access_mode($event)">[[__("mode_public")]]
<input type="radio" name="access_mode" value="public" v-model="access_settings_space.access_mode" v-on:click="save_space_access_mode($event)"><%=__("mode_public")%>
</label>
</div>
<div class="modal-section" v-if="active_space" style="padding-bottom:10px">
<h4 class="text-left"><span class="icon icon-link icon-sm"></span> [[__("access_editor_link")]] </h4>
<h4 class="text-left"><span class="icon icon-link icon-sm"></span> <%=__("access_editor_link")%> </h4>
<p class="text-left">
[[__("access_editor_link_desc")]]
<%=__("access_editor_link_desc")%>
</p>
<div class="input-group org-add-form">
@@ -30,7 +30,7 @@
<span class="input-group-btn">
<span id="org-add-member" class="btn btn-lg btn-primary clipboard-btn" data-clipboard-target="#editorurl2" v-clipboard>
[[__("copy")]]
<%=__("copy")%>
</span>
</span>
</div>
@@ -41,13 +41,13 @@
type="checkbox"
v-bind:checked="access_settings_space.editors_locking"
v-model="access_settings_space.editors_locking" v-on:change="save_space_editors_locking($event)" />
[[__("access_anonymous_edit_blocking")]]
<%=__("access_anonymous_edit_blocking")%>
</label>
</div>
<!-- subsection "invite" -->
<div id="new-editor" class="modal-section" style="padding-bottom:20px">
<h4 class="text-left"><span class="icon icon-user-add icon-sm"></span> [[__('access_new_members')]]</h4>
<h4 class="text-left"><span class="icon icon-user-add icon-sm"></span> <%=__('access_new_members')%></h4>
<div class="form-group">
<span class="error-note" v-if="invite_email_error">{{invite_email_error}}</span>
@@ -55,27 +55,27 @@
v-bind:class="{error: !!invite_email_error}"
id="invitee_email"
v-model="invite_email"
type="email" placeholder="[[__('invite_emails')]]">
type="email" placeholder="<%=__('invite_emails')%>">
<textarea id="invite-message" class="input input-block input-white overflow-y-scroll"
v-model="invite_message" placeholder="[[__('optional_message')]]" name="Message"></textarea>
v-model="invite_message" placeholder="<%=__('optional_message')%>" name="Message"></textarea>
<select class="input input-white" v-model="invite_member_role">
<option value="viewer">[[__("role_viewer")]]</option>
<option value="editor">[[__("role_editor")]]</option>
<option value="admin">[[__("role_admin")]]</option>
<option value="viewer"><%=__("role_viewer")%></option>
<option value="editor"><%=__("role_editor")%></option>
<option value="admin"><%=__("role_admin")%></option>
</select>
</div>
<div class="form-group">
<button class="btn btn-primary btn-md" v-on:click="invite_member(access_settings_space, invite_email, invite_message, invite_member_role)"> [[__("invite")]] </button>
<button class="btn btn-primary btn-md" v-on:click="invite_member(access_settings_space, invite_email, invite_message, invite_member_role)"> <%=__("invite")%> </button>
</div>
</div>
<!-- subsection "editors list" -->
<div class="modal-section" style="padding-bottom:60px">
<h4 class="text-left"><span class="icon icon-user-group icon-sm"></span> [[__("access_current_members")]]</h4>
<h4 class="text-left"><span class="icon icon-user-group icon-sm"></span> <%=__("access_current_members")%></h4>
<table class="table">
<tr>
<th></th>
@@ -102,23 +102,23 @@
<td>
<div class="form-group">
<select class="input input-sm" v-model="member.role" v-on:change="update_member(access_settings_space, member, $event.currentTarget.value)" style="width:auto">
<option value="viewer">[[__("role_viewer")]]</option>
<option value="editor">[[__("role_editor")]]</option>
<option value="admin">[[__("role_admin")]]</option>
<option value="viewer"><%=__("role_viewer")%></option>
<option value="editor"><%=__("role_editor")%></option>
<option value="admin"><%=__("role_admin")%></option>
</select>
</div>
</td>
<td>
<button class="btn btn-sm btn-stroke-darken" v-on:click="remove_member(access_settings_space, member)">[[__("delete")]]</button>
<button class="btn btn-sm btn-stroke-darken" v-on:click="remove_member(access_settings_space, member)"><%=__("delete")%></button>
</td>
</tr>
</table>
<p class="text-left" v-if="!access_settings_memberships.length">
[[__("access_no_members")]]
<%=__("access_no_members")%>
</p>
<div class="form-group" style="padding-top: 40px">
<button class="btn btn-primary btn-md" v-on:click="close_modal();"> [[__("ok")]] </button>
<button class="btn btn-primary btn-md" v-on:click="close_modal();"> <%=__("ok")%> </button>
</div>
</div>

View File

@@ -6,13 +6,13 @@
<button type="button" class="btn btn-icon btn-light btn-round close" v-on:click="close_modal()">
<span class="icon icon-cross-1"></span>
</button>
<h4 class="modal-title">[[__("folder_settings")]]</h4>
<h4 class="modal-title"><%=__("folder_settings")%></h4>
</div>
<div class="modal-body">
<div class="modal-section">
<div class="form-group">
<label class="label">[[__("title")]]</label>
<label class="label"><%=__("title")%></label>
<input type="text" class="input" v-model="active_folder.name">
</div>
@@ -26,13 +26,13 @@
<div class="form-group text-center">
<label class="file btn btn-sm btn-round btn-stroke-darken">
<input type="file" id="file" v-on:change="save_folder_avatar_image(this);">
<span>[[__("upload_cover_image")]]</span>
<span><%=__("upload_cover_image")%></span>
</label>
</div>
</div>
<div class="modal-section">
<h4 class="modal-title" style="padding-top:0px">[[__("access_caption")]]</h4>
<h4 class="modal-title" style="padding-top:0px"><%=__("access_caption")%></h4>
</div>
</div>

View File

@@ -14,30 +14,30 @@
</span>
<div class="btn-group light round" v-if="zones.length">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_previous_zone()" title="[[__("Previous Zone")]]">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_previous_zone()" title="<%=__("Previous Zone")%>">
<span class="icon icon-triangle-4-left"></span>
</button>
<button class="btn btn-md btn-divider"></button>
<button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_next_zone()" title="[[__("Next Zone")]]">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_next_zone()" title="<%=__("Next Zone")%>">
<span class="icon icon-triangle-4-right"></span>
</button>
</div>
<!--div class="btn-group light" id="meta-toggle" style="margin-right:10px">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="toggle_meta()" title="[[__("chat")]]">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="toggle_meta()" title="<%=__("chat")%>">
<span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span>
<span class="icon icon-messages"></span>
</button>
</div-->
</div>
{% include "./tool/toolbar-elements.html" %}
{% include "./tool/toolbar-object.html" %}
<%- include("./tool/toolbar-elements.html") %>
<%- include("./tool/toolbar-object.html") %>
<div v-if="active_space && active_space_loaded">
<div id="lasso"></div>
<!-- <div id="lasso"></div> -->
<div class="snap-ruler-h" v-bind:style="{top:snap_ruler_y+'px'}"></div>
<div class="snap-ruler-v" v-bind:style="{left:snap_ruler_x+'px'}"></div>
<div class="space-empty" v-cloak v-if="active_view == 'space' && !present_mode && active_space_artifacts.length == 0">
@@ -79,7 +79,7 @@
'background-color': ''+active_space.background_color,
'margin-left': bounds_margin_horiz + 'px',
'margin-top': bounds_margin_vert + 'px'}" >
<div id="lasso"></div>
<div v-for="a in active_space_artifacts"
v-bind:style="a.view.style" v-bind:class="a.view.classes"
v-bind:class="{text-editing:(editing_artifact_id==a._id && (a.view.major_type=='text' || a.view.major_type=='shape'))}"
@@ -141,8 +141,8 @@
<div class="progress" v-bind:style="{width: a.view.progress+'%'}"></div>
<div class="progress-text">{{a.description}}</div>
<video v-if="a.mime == 'image/gif' && a.payload_alternatives && a.payload_alternatives.length > 0" preload autoplay loop>
<source v-for="rep in a.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
<video v-if="a.mime == 'image/gif' && a.view.payload_alternatives && a.view.payload_alternatives.length > 0" preload autoplay loop>
<source v-for="rep in a.view.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
</video>
<span v-if="a.view.link.length>0" class="link-wrapper">
@@ -151,10 +151,10 @@
</div>
<!-- video -->
<div v-if="a.view.major_type == 'video'" v-videoplayer="a" class="video" v-bind:style="a.view.inner_style + ';background-image: url('+a.view.thumbnail_uri+');'">
<div v-if="a.view.major_type == 'video'" v-videoplayer="a" class="video" v-bind:style="a.view.inner_style">
<video preload="metadata" v-bind:poster="a.view.thumbnail_uri">
<source v-for="rep in a.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
<source v-if="a.payload_uri && a.mime" v-bind:src="a.payload_uri" v-bind:type="a.mime" />
<source v-for="rep in a.view.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
<source v-if="a.view.payload_uri && a.view.mime" v-bind:src="a.view.payload_uri" v-bind:type="a.view.mime" />
</video>
<div class="tl-controls">
@@ -181,11 +181,11 @@
<div v-if="a.view.major_type == 'audio'" v-audioplayer="a" class="audio" v-bind:style="a.view.inner_style">
<audio>
<source v-for="alt in a.payload_alternatives | orderBy 'mime' -1" v-bind:src="alt.payload_uri" v-bind:type="alt.mime"/>
<source v-bind:src="a.payload_uri" v-bind:type="a.mime" v-if="a.payload_uri"/>
<source v-for="alt in a.view.payload_alternatives | orderBy 'mime' -1" v-bind:src="alt.payload_uri" v-bind:type="alt.mime"/>
<source v-bind:src="a.view.payload_uri" v-bind:type="a.view.mime" v-if="a.view.payload_uri"/>
</audio>
<div class="timeline" v-show="a.h>=64 && a.w>=170" v-bind:style="{'background-image': 'url(' + a.payload_thumbnail_web_uri +')'}">
<div class="timeline" v-show="a.h>=64 && a.w>=170" v-bind:style="{'background-image': 'url(' + a.view.thumbnail_uri +')'}">
<div class="tl-current-time" v-bind:style="{width: a.player_view.current_time_float*100 + '%'}"></div>
<div class="tl-inpoint" v-bind:style="{left: a.player_view.inpoint_float*100 + '%'}" v-if="a.player_view.inpoint_float>0.0"></div>
<div class="tl-outpoint" v-bind:style="{left: a.player_view.outpoint_float*100 + '%'}"></div>

View File

@@ -5,47 +5,47 @@
<h4>Spacedeck Team Management</h4>
<div v-if="!user.team" class="dialog-section text-left">
You are not in a team yet. Please upgrade first.</h2>
You are not in a team yet. Please upgrade first.
</div>
<div v-if="user.team">
<div class="dialog-section">
<h4 class="text-left">[[__("team_name")]]</h4>
<h4 class="text-left"><%=__("team_name")%></h4>
<div class="input-group org-add-form">
<input id="org-member-emails" v-model="user.team.name" class="form-control input input-lg" type="text">
<span class="input-group-btn">
<span id="org-add-member" class="btn btn-lg btn-primary" v-on:click=" team_save()">
[[__("save")]]
<%=__("save")%>
</span>
</span>
</div>
</div>
<div class="dialog-section">
<h4 class="text-left">[[__("subdomain")]]</h4>
<h4 class="text-left"><%=__("subdomain")%></h4>
<div class="input-group org-add-form">
<input v-model="user.team.subdomain" class="form-control input input-lg" type="text">
<span class="input-group-btn">
<span id="org-add-member" class="btn btn-lg btn-primary" v-on:click=" team_save()">
[[__("save")]]
<%=__("save")%>
</span>
</span>
</div>
</div>
<div class="dialog-section text-left">
<h4>Members</h4>
<h4><%=__("members")%></h4>
<p>
New members will get an invitation email. After the invitation was used, the member is active. The number of active members in your team will affect your monthly charge.
</p>
<div class="input-group org-add-form">
<input id="org-member-emails" v-model="team_emails" class="form-control input input-lg" type="email" placeholder="[[__("team_adresses")]]">
<input id="org-member-emails" v-model="team_emails" class="form-control input input-lg" type="email" placeholder="<%=__("team_adresses")%>">
<span class="input-group-btn">
<span id="org-add-member" class="btn btn-lg btn-primary org-save" v-on:click=" team_invite_members(team_emails)">
<span v-if="!team_email_invited">[[__("add")]]</span>
<span v-if="team_email_invited">[[__("invited")]]</span>
<span v-if="!team_email_invited"><%=__("add")%></span>
<span v-if="team_email_invited"><%=__("invited")%></span>
</span>
</span>
</div>
@@ -53,9 +53,9 @@
<table class="table table-striped table-condensed" v-if="team_members.length">
<thead>
<tr>
<th> [[__("email")]] </th>
<th> [[__("name")]] </th>
<th> [[__("role")]] </th>
<th> <%=__("email")%> </th>
<th> <%=__("name")%> </th>
<th> <%=__("role")%> </th>
<th></th>
</tr>
</thead>
@@ -73,14 +73,14 @@
</td>
<td>
<span v-if="is_admin(u)">[[__("role_admin")]]</span>
<span v-if="!is_admin(u)">[[__("role_member")]]</span>
<span v-if="is_admin(u)"><%=__("role_admin")%></span>
<span v-if="!is_admin(u)"><%=__("role_member")%></span>
</td>
<td>
<span v-if="u._id != user._id" class="btn btn-sm btn-danger" v-on:click="team_remove_member(u)">[[__("remove")]]</span>
<span v-if="(u._id != user._id) && !is_admin(u)" class="btn btn-sm" v-on:click="team_promote_member(u)">[[__("promote")]]</span>
<span v-if="(u._id != user._id) && is_admin(u)" class="btn btn-sm" v-on:click="team_demote_member(u)">[[__("demote")]]</span>
<span v-if="u._id != user._id" class="btn btn-sm btn-danger" v-on:click="team_remove_member(u)"><%=__("remove")%></span>
<span v-if="(u._id != user._id) && !is_admin(u)" class="btn btn-sm" v-on:click="team_promote_member(u)"><%=__("promote")%></span>
<span v-if="(u._id != user._id) && is_admin(u)" class="btn btn-sm" v-on:click="team_demote_member(u)"><%=__("demote")%></span>
</td>
</tr>

View File

@@ -1,8 +1,8 @@
<div class="dialog-tabs-wrapper">
<div class="dialog-tabs">
<div class="dialog-tab" v-bind:class="{open:background_mode=='image'}" v-on:click="background_mode='image'"><span>[[__("background_image_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:background_mode=='color'}" v-on:click="background_mode='color'"><span>[[__("background_color_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:background_mode=='image'}" v-on:click="background_mode='image'"><span><%=__("background_image_caption")%></span></div>
<div class="dialog-tab" v-bind:class="{open:background_mode=='color'}" v-on:click="background_mode='color'"><span><%=__("background_color_caption")%></span></div>
</div>
</div>
@@ -69,9 +69,9 @@
<!--div class="dialog-section" v-show="background_mode=='color'">
<div class="tab-switch round options-2" v-bind:class="{'option-2':color_mode=='picker'}">
<div class="options">
<span class="option" v-on:click="activate_color_mode('palette')">[[__("palette")]]</span>
<span class="option" v-on:click="activate_color_mode('palette')"><%=__("palette")%></span>
<span class="option" v-on:click="activate_color_mode('picker')">
<span>[[__("picker")]]</span>
<span><%=__("picker")%></span>
</span>
</div>
<span class="option-highlight"></span>
@@ -79,7 +79,7 @@
</div-->
<div class="" v-show="background_mode=='image'" v-if="active_space">
<div class="background-image" v-bind:style="{height: '233px', 'background-image':'url('+active_space.background_uri+')', 'margin': '6px', 'border-radius': '3px'}" v-if="active_space.background_uri && !space_background_uploading">
<div class="background-image" v-bind:style="{height: '233px', width: '233px', 'background-image':'url('+active_space.background_uri+')', 'margin': '6px', 'border-radius': '3px'}" v-if="active_space.background_uri && !space_background_uploading">
</div>
<div class="progress state-processing" v-if="space_background_uploading">
@@ -91,7 +91,7 @@
<span class="icon icon-picture-upload"></span>
<input id="background-uploader" type="file" accept="image/*" v-on:change="handle_section_background_upload($event)">
</label>
<p>[[__("upload_background_caption")]]</p>
<p><%=__("upload_background_caption")%></p>
</div>
<div class="dialog-section no-p no-flex" v-if="active_space.background_uri">

View File

@@ -77,9 +77,9 @@
<!--div class="dialog-section no-b" style="margin-top:-10px">
<div class="tab-switch round options-2" v-bind:class="{'option-2':color_mode=='picker'}">
<div class="options">
<span class="option" v-on:click="activate_color_mode('palette')">[[__("palette")]]</span>
<span class="option" v-on:click="activate_color_mode('palette')"><%=__("palette")%></span>
<span class="option" v-on:click="activate_color_mode('picker')">
<span>[[__("picker")]]</span>
<span><%=__("picker")%></span>
</span>
</div>
<span class="option-highlight"></span>
@@ -123,7 +123,7 @@
<div class="dialog-section no-p-b no-p-h" v-show="opened_dialog=='color-text'">
<div class="input-row">
<div class="form-group no-m">
<label class="label label-sm text-center">[[__("font_size")]]</label>
<label class="label label-sm text-center"><%=__("font_size")%></label>
<input class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9]" maxlength="64" v-model="active_style.font_size">
<button tabindex="-1" class="input-drag btn btn-transparent btn-icon" style="cursor: ns-resize;" v-sd-fader="true" sd-fader-var-y="active_style.font_size" sd-fader-min-y="30" sd-fader-max-y="200" sd-fader-sens="5">
@@ -133,7 +133,7 @@
</div>
<!--div class="form-group no-m">
<label class="label label-sm text-center">[[__("line_height")]]</label>
<label class="label label-sm text-center"><%=__("line_height")%></label>
<input disabled class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9\.]" maxlength="64" v-model="active_style.line_height">
<button tabindex="-1" class="input-drag btn btn-transparent btn-icon" style="cursor: ns-resize;" v-sd-fader="true" sd-fader-var-y="active_style.line_height" sd-fader-min-y="0.5" sd-fader-max-y="4" sd-fader-step="0.1">
<span class="icon icon-triangles-vertical"></span>

View File

@@ -1,36 +1,36 @@
<div class="dialog-section no-b no-p" v-if="active_space_loaded">
<div class="btn-group">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="delete_selected_artifacts()">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="delete_selected_artifacts()" title="<%=__("delete")%>">
<span class="icon icon-trash"></span>
<span class="icon-label">[[__("delete")]]</span>
<span class="icon-label"><%=__("delete")%></span>
</button>
<button class="btn btn-divider"></button>
<button class="btn btn-transparent btn-icon-labeled" v-on:click="toggle_lock_of_selected_artifacts()" v-if="active_space_role=='admin'">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="toggle_lock_of_selected_artifacts()" v-if="active_space_role=='admin'" title="<%=__("lock")%>">
<span class="icon icon-lock-closed"></span>
<span class="icon-label">[[__("lock")]]</span>
<span class="icon-label"><%=__("lock")%></span>
</button>
<button class="btn btn-transparent btn-icon-labeled" v-on:click="toggle_lock_of_selected_artifacts()" v-if="active_space_role=='admin'">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="toggle_lock_of_selected_artifacts()" v-if="active_space_role=='admin'" title="<%=__("unlock")%>">
<span class="icon icon-lock-open"></span>
<span class="icon-label">[[__("unlock")]]</span>
<span class="icon-label"><%=__("unlock")%></span>
</button>
<button class="btn btn-transparent btn-icon-labeled" v-on:click="duplicate_selected_artifacts()">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="duplicate_selected_artifacts()" title="<%=__("copy")%>">
<span class="icon icon-duplicate"></span>
<span class="icon-label">[[__("copy")]]</span>
<span class="icon-label"><%=__("copy")%></span>
</button>
<button class="btn btn-transparent btn-icon-labeled" v-on:click="create_link_on_selected_artifacts()">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="create_link_on_selected_artifacts()" title="<%=__("link")%>">
<span class="icon icon-link"></span>
<span class="icon-label">[[__("link")]]</span>
<span class="icon-label"><%=__("link")%></span>
</button>
<span v-if="first_selected_artifact">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="download_selected_artifacts()" v-if="selection_metrics.count==1 && first_selected_artifact.payload_uri">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="download_selected_artifacts()" v-if="selection_metrics.count==1 && first_selected_artifact.payload_uri" title="<%=__("download")%>">
<span class="icon icon-download"></span>
<span class="icon-label">[[__("download")]]</span>
<span class="icon-label"><%=__("download")%></span>
</button>
</span>
</div>

View File

@@ -30,7 +30,7 @@
<!-- generic search box placeholder -->
<div class="relative">
<span class="icon icon-zoom absolute-top-left no-events" style="margin-top: -2px;"></span>
<input type="text" v-model="generic_search_query" v-focus class="input input-white input-round input-block no-b p-l-60" placeholder="[[__("search_media_placeholder")]]" v-on="keyup: search_generic(generic_search_query) | key enter">
<input type="text" v-model="generic_search_query" v-focus class="input input-white input-round input-block no-b p-l-60" placeholder="<%=__("search_media_placeholder")%>" v-on="keyup: search_generic(generic_search_query) | key enter">
</div>
<!-- <button type="button" class="btn btn-sm btn-icon btn-round btn-darken absolute-top-right clear-search" style="margin-right: 42px; margin-top: 40px;">

View File

@@ -1,46 +1,46 @@
<h4 class="dialog-title">[[__("tool_shape")]]</h4>
<h4 class="dialog-title"><%=__("tool_shape")%></h4>
<div id="shapes">
<div class="dialog-section">
<div class="btn-group">
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('ellipse',$event)">
<span class="icon icon-shape-circle"></span>
<span class="icon-label">[[__("tool_circle")]]</span>
<span class="icon-label"><%=__("tool_circle")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('hexagon',$event)">
<span class="icon icon-shape-hexagon"></span>
<span class="icon-label">[[__("tool_hexagon")]]</span>
<span class="icon-label"><%=__("tool_hexagon")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('square',$event)">
<span class="icon icon-shape-square"></span>
<span class="icon-label">[[__("tool_square")]]</span>
<span class="icon-label"><%=__("tool_square")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('speechbubble',$event)">
<span class="icon icon-shape-bubble"></span>
<span class="icon-label">[[__("tool_bubble")]]</span>
<span class="icon-label"><%=__("tool_bubble")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('cloud',$event)">
<span class="icon icon-shape-cloud"></span>
<span class="icon-label">[[__("tool_cloud")]]</span>
<span class="icon-label"><%=__("tool_cloud")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('burst',$event)">
<span class="icon icon-shape-burst"></span>
<span class="icon-label">[[__("tool_burst")]]</span>
<span class="icon-label"><%=__("tool_burst")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('star',$event)">
<span class="icon icon-shape-star"></span>
<span class="icon-label">[[__("tool_star")]]</span>
<span class="icon-label"><%=__("tool_star")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('heart',$event)">
<span class="icon icon-shape-heart"></span>
<span class="icon-label">[[__("tool_heart")]]</span>
<span class="icon-label"><%=__("tool_heart")%></span>
</button>
</div>

View File

@@ -1,12 +1,12 @@
<h4 class="dialog-title">[[__("text_formats")]]</h4>
<h4 class="dialog-title"><%=__("text_formats")%></h4>
<ul class="select-list overflow-y-scroll" style="min-width: 200px;">
<li id="note-format-div" v-on="mousedown:apply_formatting($event, 'p')"> <span>[[__("format_p")]]</span> </li>
<li id="note-format-ul" v-on="mousedown:apply_formatting($event, 'insertunorderedlist')"> <span>[[__("format_bullets")]]</span> </li>
<li id="note-format-ol" v-on="mousedown:apply_formatting($event, 'insertorderedlist')"> <span>[[__("format_numbers")]]</span> </li>
<li id="note-format-h1" v-on="mousedown:apply_formatting($event, 'h1')"> <span>[[__("format_h1")]]</span> </li>
<li id="note-format-h2" v-on="mousedown:apply_formatting($event, 'h2')"> <span>[[__("format_h2")]]</span> </li>
<li id="note-format-h3" v-on="mousedown:apply_formatting($event, 'h3')"> <span>[[__("format_h3")]]</span> </li>
<li id="note-format-div" v-on="mousedown:apply_formatting($event, 'p')"> <span><%=__("format_p")%></span> </li>
<li id="note-format-ul" v-on="mousedown:apply_formatting($event, 'insertunorderedlist')"> <span><%=__("format_bullets")%></span> </li>
<li id="note-format-ol" v-on="mousedown:apply_formatting($event, 'insertorderedlist')"> <span><%=__("format_numbers")%></span> </li>
<li id="note-format-h1" v-on="mousedown:apply_formatting($event, 'h1')"> <span><%=__("format_h1")%></span> </li>
<li id="note-format-h2" v-on="mousedown:apply_formatting($event, 'h2')"> <span><%=__("format_h2")%></span> </li>
<li id="note-format-h3" v-on="mousedown:apply_formatting($event, 'h3')"> <span><%=__("format_h3")%></span> </li>
</ul>
<!--

View File

@@ -3,7 +3,7 @@
<div class="btn-group light vertical">
<a class="btn btn-icon btn-transparent"
title="[[__("home")]]" href="/spaces"
title="<%=__("home")%>" href="/spaces"
v-if="(!active_space.parent_space_id && !guest_nickname && !embedded)">
<span class="icon icon-folder"></span>
</a>
@@ -19,69 +19,69 @@
<button class="btn btn-divider"></button>
<div class="dropdown top left light" v-bind:class="{open:opened_dialog=='shapes'}">
<div class="btn-collapse in">
<div class="btn-collapse in" title="<%=__("tool_shape")%>">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='shapes'}" v-on:click="open_dialog('shapes')">
<span class="icon icon-shapes"></span>
<span class="icon-label">[[__("tool_shape")]]</span>
<span class="icon-label"><%=__("tool_shape")%></span>
</button>
</div>
<div class="dialog">
{% include "./shapes.html" %}
<%- include("./shapes.html") %>
</div>
</div>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_scribble()" v-bind:class="{active:active_tool=='scribble'}">
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_scribble()" v-bind:class="{active:active_tool=='scribble'}" title="<%=__("tool_scribble")%>">
<span class="icon icon-tool-scribble"></span>
<span class="icon-label">[[__("tool_scribble")]]</span>
<span class="icon-label"><%=__("tool_scribble")%></span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_arrow()" v-bind:class="{active:active_tool=='arrow'}">
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_arrow()" v-bind:class="{active:active_tool=='arrow'}" title="<%=__("tool_arrow")%>">
<span class="icon icon-tool-arrow"></span>
<span class="icon-label">[[__("tool_arrow")]]</span>
<span class="icon-label"><%=__("tool_arrow")%></span>
</button>
<div class="dropdown bottom light center">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="handle_insert_image_url()" v-on:touchstart="handle_insert_image_url()">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="handle_insert_image_url()" v-on:touchstart="handle_insert_image_url()" title="<%=__("media")%>">
<span class="icon icon-upload"></span>
<span class="icon-label" >Media</span>
<span class="icon-label" ><%=__("media")%></span>
</button>
</div>
</div>
<div class="dropdown bottom light center">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="active_tool='note'" v-bind:class="{active:active_tool=='note'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="start_adding_note()" v-bind:class="{active:active_tool=='note'}" title="<%=__("tool_text")%>">
<span class="icon icon-tool-text"></span>
<span class="icon-label">[[__("tool_text")]]</span>
<span class="icon-label"><%=__("tool_text")%></span>
</button>
</div>
</div>
<div class="dropdown top left light">
<div class="btn-collapse">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='image'}" v-on:click="open_dialog('image')">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='image'}" v-on:click="open_dialog('image')" title="<%=__("image")%>">
<span class="icon icon-picture"></span>
<span class="icon-label">[[__("image")]]</span>
<span class="icon-label"><%=__("image")%></span>
</button>
</div>
<div class="dialog">
{% include "./image.html" %}
<%- include("./image.html") %>
</div>
</div>
<div class="dropdown top left light" v-bind:class="{open:opened_dialog=='zones'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='zones'}" v-on:click="open_dialog('zones')">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='zones'}" v-on:click="open_dialog('zones')" title="<%=__("tool_zones")%>">
<span class="icon icon-zone"></span>
<span class="icon-label">[[__("tool_zones")]]</span>
<span class="icon-label"><%=__("tool_zones")%></span>
</button>
</div>
<div class="dialog">
{% include "./zones.html" %}
<%- include("./zones.html") %>
</div>
</div>
@@ -89,35 +89,36 @@
<div class="dropdown top left center" v-show="logged_in" v-bind:class="{open:opened_dialog=='background'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='background'}" v-on:click="open_dialog('background')">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='background'}" v-on:click="open_dialog('background')" title="<%=__("tool_canvas")%>">
<span class="letter">bg</span>
<span class="icon-label">[[__("tool_canvas")]]</span>
<span class="icon-label"><%=__("tool_canvas")%></span>
</button>
</div>
<div class="dialog">
{% include "./background.html" %}
<%- include("./background.html") %>
</div>
</div>
<button class="btn btn-transparent btn-icon-labeled"
v-if="active_space_role=='admin'"
v-on:click="activate_access()">
v-on:click="activate_access()"
title="<%= __('share') %>">
<span class="icon icon-share"></span>
<span class="icon-label">[[ __('share') ]]</span>
<span class="icon-label"><%= __('share') %></span>
</button>
<!--
<li v-on:click="edit_space_title()" v-if="logged_in">
<span>
<span class="icon icon-sm icon-tag"></span>
<span>[[ __('rename') ]]</span>
<span><%= __('rename') %></span>
</span>
</li>
-->
<button class="btn btn-transparent btn-icon-labeled" title="Start Presentation (others follow what you see)" v-on:click="toggle_present_mode()" v-bind:class="{open:present_mode}">
<span class="icon icon-presentation"></span>
<span class="icon-label">[[ __('present') ]]</span>
<span class="icon-label"><%= __('present') %></span>
</button>
</div>

View File

@@ -30,7 +30,7 @@
<span class="jewel" v-bind:style="{'border-color':active_style.text_color}">{{active_style.font_size}}</span>
</button>
<div class="dialog">
{% include "./color.html" %}
<%- include("./color.html") %>
</div>
</div>
<!-- <button class="btn btn-transparent btn-icon-labeled">
@@ -43,55 +43,55 @@
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-styles'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-styles')" v-bind:class="{open : opened_dialog=='text-styles'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-styles')" v-bind:class="{open : opened_dialog=='text-styles'}" title="<%=__("tool_styles")%>">
<span class="icon icon-text-styles"></span>
<span class="icon-label">Styles</span>
<span class="icon-label"><%=__("tool_styles")%></span>
</button>
</div>
<div class="dialog">
{% include "./text-styles.html" %}
<%- include("./text-styles.html") %>
</div>
</div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='type-align'}">
<div class="btn-collapse" v-bind:class="{in:selection_metrics.contains_text}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('type-align')" v-bind:class="{open : opened_dialog=='type-align'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('type-align')" v-bind:class="{open : opened_dialog=='type-align'}" title="<%=__("tool_align")%>">
<span class="icon icon-text-align-left-alt"></span>
<span class="icon-label">[[__("tool_align")]]</span>
<span class="icon-label"><%=__("tool_align")%></span>
</button>
</div>
<div class="dialog">
{% include "./text-align.html" %}
<%- include("./text-align.html") %>
</div>
</div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='layout'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('layout')" v-bind:class="{open : opened_dialog=='layout'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('layout')" v-bind:class="{open : opened_dialog=='layout'}" title="<%=__("tool_layout")%>">
<span class="icon icon-cluster"></span>
<span class="icon-label">[[__("tool_layout")]]</span>
<span class="icon-label"><%=__("tool_layout")%></span>
</button>
</div>
<div class="dialog">
{% include "./layout.html" %}
<%- include("./layout.html") %>
</div>
</div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-settings'}">
<div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-settings')" v-bind:class="{open : opened_dialog=='text-settings'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-settings')" v-bind:class="{open : opened_dialog=='text-settings'}" title="<%=__("tool_font")%>">
<span class="icon icon-text-typeface"></span>
<span class="icon-label">Font</span>
<span class="icon-label"><%=__("tool_font")%></span>
</button>
</div>
<div class="dialog">
{% include "./text-digits.html" %}
<%- include("./text-digits.html") %>
</div>
</div>
@@ -100,11 +100,11 @@
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='object-options'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('object-options')" v-bind:class="{open : opened_dialog=='object-options'}">
<span class="icon icon-cogwheel"></span>
<span class="icon-label">[[__("more")]]</span>
<span class="icon-label"><%=__("more")%></span>
</button>
<div class="dialog no-min-w">
{% include "./object-options.html" %}
<%- include("./object-options.html") %>
</div>
</div>

View File

@@ -9,7 +9,7 @@
</div>
<div class="dialog">
{% include "./text-formats.html" %}
<%- include "./text-formats.html" %>
</div>
</div>
@@ -23,7 +23,7 @@
</button>
<div class="dialog">
{% include "./text-digits.html" %}
<%- include "./text-digits.html" %>
</div>
</div>
@@ -36,7 +36,7 @@
</button>
<div class="dialog">
{% include "./text-align.html" %}
<%- include "./text-align.html" %>
</div>
</div>
@@ -47,7 +47,7 @@
</button>
<div class="dialog">
{% include "./text-styles.html" %}
<%- include "./text-styles.html" %>
</div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<h4 class="dialog-title">[[__("tool_zones")]]</h4>
<h4 class="dialog-title"><%=__("tool_zones")%></h4>
<div id="zones" style="max-height:500px;overflow-y:scroll">
<div class="dialog-section">
@@ -6,7 +6,7 @@
Turn your Space into a zooming presentation by placing some Zones and switch through them when presenting.
</p-->
<button v-on:click="add_zone()" class="btn btn-sm btn-dark">[[__("add_zone")]]</button>
<button v-on:click="add_zone()" class="btn btn-sm btn-dark"><%=__("add_zone")%></button>
</div>
<div class="dialog-section no-p" v-for="z in zones | orderBy 'order'" style="white-space: nowrap;text-align:left;cursor:pointer" v-on:click="zoom_to_zone(z)">

View File

@@ -1,10 +0,0 @@
{% extends '../layouts/outer.html' %}
{% block title %}[[ __("contact") ]]{% endblock %}
{% block content %}
<div class="landing-box">
</div>
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends '../layouts/outer.html' %}
{% block title %}[[ __("privacy") ]]{% endblock %}
{% block content %}
<div class="landing-box">
</div>
{% endblock %}

View File

@@ -1,8 +0,0 @@
{% extends '../layouts/outer.html' %}
{% block title %}[[ __("terms") ]]{% endblock %}
{% block content %}
<div class="landing-box">
</div>
{% endblock %}

View File

@@ -1,20 +0,0 @@
<html>
<body>
<h2>[[ __("folder") ]]: [[space.name]]</h2>
<table class="table table-striped" >
<tr>
<th>[[__("created")]]</th>
<th>[[__("name")]]</th>
<th>[[__("link")]]</th>
</tr>
{% for s in subspaces %}
<tr>
<td>[[ s.created_at | date('d.m.Y H:i') ]]</td>
<td>[[ s.name ]]</td>
<td>[[ s.ae_link ]]</td>
</tr>
{% endfor %}
</table>
</body>
</html>

View File

@@ -8,17 +8,14 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" />
<link href="/images/favicon.png" rel="icon" type="image/x-icon" />
<link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
<link rel="stylesheet" href="/stylesheets/style.css">
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script>
window.socket_auth = '[[socket_auth]]';
window.browser_lang = '[[locale]]';
window.csrf_token = '[[csrf_token]]';
//window.browser_lang = '< %= locale %>';
var ENV = {
name: 'development',
webHost: location.host,
@@ -67,16 +64,15 @@
</head>
<body id="main" v-bind:class="{'present-mode':present_mode,'modal-open':active_modal}" v-on:click="handle_body_click($event)">
{% include "./partials/login.html" %}
{% include "./partials/space.html" %}
{% include "./partials/folders.html" %}
{% include "./partials/team.html" %}
{% include "./partials/account.html" %}
{% include "./partials/meta.html" %}
{% include "./partials/meta-folder.html" %}
{% include "./partials/modal/access.html" %}
{% include "./partials/modal/folder-settings.html" %}
<%- include("./partials/login.html") %>
<%- include("./partials/space.html") %>
<%- include("./partials/folders.html") %>
<%- include("./partials/team.html") %>
<%- include("./partials/account.html") %>
<%- include("./partials/meta.html") %>
<%- include("./partials/meta-folder.html") %>
<%- include("./partials/modal/access.html") %>
<%- include("./partials/modal/folder-settings.html") %>
</body>
<script type="text/javascript">
@@ -84,8 +80,12 @@
window.locales.en = {};
window.locales.de = {};
window.locales.fr = {};
window.locales.en.translation = {% include "./../locales/en.js" %};
window.locales.de.translation = {% include "./../locales/de.js" %};
window.locales.fr.translation = {% include "./../locales/fr.js" %};
window.locales.oc = {};
window.locales.es = {};
window.locales.en.translation = <%- include("./../locales/en.js") %>;
window.locales.de.translation = <%- include("./../locales/de.js") %>;
window.locales.fr.translation = <%- include("./../locales/fr.js") %>;
window.locales.oc.translation = <%- include("./../locales/oc.js") %>;
window.locales.es.translation = <%- include("./../locales/es.js") %>;
</script>
</html>