Compare commits
2 Commits
whiteboard
...
greenkeepe
Author | SHA1 | Date | |
---|---|---|---|
|
f7df0b7f43 | ||
|
d11eab4038 |
@ -1,14 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
.git
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
scripts
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
lib-cov
|
|
||||||
coverage
|
|
||||||
.grunt
|
|
||||||
.lock-wscript
|
|
||||||
build/Release
|
|
||||||
node_modules
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,8 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
public/stylesheets/*
|
||||||
javascripts/maps
|
javascripts/maps
|
||||||
javascripts/spacedeck.js
|
javascripts/spacedeck.js
|
||||||
public/stylesheets/*.css
|
|
||||||
database.sqlite
|
|
||||||
*.swp
|
|
||||||
*~
|
|
||||||
|
|
||||||
|
29
Dockerfile
29
Dockerfile
@ -1,29 +0,0 @@
|
|||||||
FROM spacedeck/docker-baseimage:latest
|
|
||||||
ENV NODE_ENV production
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
COPY package.json /usr/src/app/
|
|
||||||
RUN npm install
|
|
||||||
RUN npm install gulp-rev-replace gulp-clean gulp-fingerprint gulp-rev gulp-rev-all gulp-rev-replace
|
|
||||||
RUN npm install -g --save-dev gulp
|
|
||||||
|
|
||||||
COPY app.js Dockerfile Gulpfile.js LICENSE /usr/src/app/
|
|
||||||
COPY config /usr/src/app/config
|
|
||||||
COPY helpers /usr/src/app/helpers
|
|
||||||
COPY locales /usr/src/app/locales
|
|
||||||
COPY middlewares /usr/src/app/middlewares
|
|
||||||
COPY models /usr/src/app/models
|
|
||||||
COPY public /usr/src/app/public
|
|
||||||
COPY routes /usr/src/app/routes
|
|
||||||
COPY styles /usr/src/app/styles
|
|
||||||
COPY views /usr/src/app/views
|
|
||||||
|
|
||||||
RUN gulp all
|
|
||||||
RUN npm cache clean
|
|
||||||
|
|
||||||
CMD [ "node", "app.js" ]
|
|
||||||
|
|
||||||
EXPOSE 9666
|
|
||||||
|
|
63
Gulpfile.js
63
Gulpfile.js
@ -1,13 +1,62 @@
|
|||||||
const gulp = require('gulp')
|
var gulp = require('gulp');
|
||||||
const sass = require('gulp-sass')
|
var sass = require('gulp-sass');
|
||||||
const concat = require('gulp-concat')
|
var concat = require('gulp-concat');
|
||||||
|
var server = require('gulp-express');
|
||||||
|
var nodemon = require('gulp-nodemon');
|
||||||
|
var revReplace = require("gulp-rev-replace");
|
||||||
|
var clean = require('gulp-clean');
|
||||||
|
|
||||||
gulp.task('styles', function(done) {
|
var child_process = require('child_process');
|
||||||
|
var path = require('path');
|
||||||
|
var uglify = require('gulp-uglify');
|
||||||
|
var fingerprint = require('gulp-fingerprint');
|
||||||
|
var rev = require('gulp-rev');
|
||||||
|
|
||||||
|
var RevAll = require('gulp-rev-all');
|
||||||
|
|
||||||
|
gulp.task('rev', () => {
|
||||||
|
var revAll = new RevAll();
|
||||||
|
return gulp.src(['public/**'])
|
||||||
|
.pipe(gulp.dest('build/assets'))
|
||||||
|
.pipe(revAll.revision())
|
||||||
|
.pipe(gulp.dest('build/assets'))
|
||||||
|
.pipe(revAll.manifestFile())
|
||||||
|
.pipe(gulp.dest('build/assets'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("all", ["styles", "uglify", "rev", "copylocales"], function(){
|
||||||
|
var manifest = gulp.src("./build/assets/rev-manifest.json");
|
||||||
|
return gulp.src("./views/**/*")
|
||||||
|
.pipe(revReplace({manifest: manifest}))
|
||||||
|
.pipe(gulp.dest("./build/views"));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('copylocales', function(){
|
||||||
|
return gulp.src('./locales/*.js').pipe(gulp.dest('./build/locales'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('clean', function () {
|
||||||
|
return gulp.src('./build').pipe(clean());
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('styles', function() {
|
||||||
gulp.src('styles/**/*.scss')
|
gulp.src('styles/**/*.scss')
|
||||||
.pipe(sass({
|
.pipe(sass({
|
||||||
errLogToConsole: true
|
errLogToConsole: true
|
||||||
}))
|
}))
|
||||||
.pipe(gulp.dest('./public/stylesheets/'))
|
.pipe(gulp.dest('./public/stylesheets/'))
|
||||||
.pipe(concat('style.css'))
|
.pipe(concat('style.css'));
|
||||||
done()
|
});
|
||||||
})
|
|
||||||
|
gulp.task('uglify', () => {
|
||||||
|
child_process.exec('sed -n \'s/.*script minify src="\\(.*\\)".*/.\\/public\\/\\1/p\' views/spacedeck.html',
|
||||||
|
function (error, stdout, stderr) {
|
||||||
|
var scripts = stdout.split('\n').map(function(p){return path.normalize(p)}).filter(function(p){return p!='.'});
|
||||||
|
console.log("scripts: ",scripts);
|
||||||
|
|
||||||
|
gulp.src(scripts)
|
||||||
|
.pipe(uglify({output:{beautify:true}}))
|
||||||
|
.pipe(concat('spacedeck.js'))
|
||||||
|
.pipe(gulp.dest('public/javascripts'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
74
README.md
74
README.md
@ -1,82 +1,60 @@
|
|||||||
# Spacedeck Open
|
# Spacedeck Open
|
||||||
|
|
||||||
![Spacedeck 6.0 Screenshot](/public/images/sd6-screenshot.png)
|
[![Greenkeeper badge](https://badges.greenkeeper.io/spacedeck/spacedeck-open.svg)](https://greenkeeper.io/)
|
||||||
|
|
||||||
This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin Güther (magegu).
|
This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin Güther (magegu). All icons and large parts of the CSS were designed by Thomas Helbig (dergraph).
|
||||||
|
|
||||||
The spacedeck.com online service was shut down on May 1st 2018. We decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted or local version.
|
As we plan to retire the subscription based service at spacedeck.com in late 2017, we decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted version.
|
||||||
|
|
||||||
[MNT Research GmbH](https://mntre.com) has restarted development of Spacedeck Open in 2020.
|
Data migration features will be added soon.
|
||||||
|
|
||||||
We appreciate filed issues, pull requests and general discussion.
|
We appreciate filed issues, pull requests and general discussion.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Create virtual whiteboards called *Spaces* with virtually unlimited size
|
- Create virtual whiteboards called "Spaces" with virtually unlimited size
|
||||||
- Drag & drop images, videos and audio from your computer or the web
|
- Drag & drop images, videos and audio from your computer or the web
|
||||||
- Write and format text with full control over fonts, colors and style
|
- Write and format text with full control over fonts, colors and style
|
||||||
- Draw, annotate and highlight with included graphical shapes
|
- Draw, annotate and highlight with included graphical shapes
|
||||||
- Turn your Space into a zooming presentation
|
- Turn your Space into a zooming presentation
|
||||||
- Collaborate in realtime with teammates, students or friends
|
- Collaborate and chat in realtime with teammates, students or friends
|
||||||
- Share Spaces on the web or via email
|
- Share Spaces on the web or via email
|
||||||
- Export your work as printable PDF or ZIP (currently being fixed, stay tuned)
|
- Export your work as printable PDF or ZIP
|
||||||
|
|
||||||
# Use Cases
|
|
||||||
|
|
||||||
- Education: Virtual classwork with multimedia
|
|
||||||
- Creative: Mood boards, Brainstorming, Design Thinking
|
|
||||||
- Visual note taking and planning
|
|
||||||
|
|
||||||
# Requirements, Installation
|
# Requirements, Installation
|
||||||
|
|
||||||
Spacedeck requires:
|
Spacedeck uses the following major building blocks:
|
||||||
|
|
||||||
- Node.js 10.x: Web Server / API. Download: https://nodejs.org
|
- Node.js 4.x (Backend / API)
|
||||||
|
- MongoDB 3.x (Datastore)
|
||||||
|
- Redis 3.x (Datastore for realtime channels)
|
||||||
|
- Vue.js (Frontend)
|
||||||
|
|
||||||
To run Spacedeck, you only need Node.JS 10.x.
|
It also has some binary dependencies for media conversion and PDF export:
|
||||||
|
|
||||||
To install all node dependencies, run (do this once):
|
- imagemagick
|
||||||
|
|
||||||
|
Currently, media files are stored in Amazon S3, so you need an Amazon AWS account and have the ```AWS_ACCESS_KEY_ID``` and ```AWS_SECRET_ACCESS_KEY``` environment variables defined. For sending emails, Amazon SES is required.
|
||||||
|
|
||||||
|
To install Spacedeck, you need node.js 4.x and a running MongoDB instance. Then, to install all node dependencies, run
|
||||||
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# Configuration
|
To rebuild the frontend CSS styles (you need to do this at least once):
|
||||||
|
|
||||||
See [config/default.json](config/default.json)
|
|
||||||
|
|
||||||
# Run (web server)
|
|
||||||
|
|
||||||
node spacedeck.js
|
|
||||||
|
|
||||||
Then open http://localhost:9666 in a web browser.
|
|
||||||
|
|
||||||
# Optional Dependencies
|
|
||||||
|
|
||||||
For advanced media conversion:
|
|
||||||
|
|
||||||
- ffmpeg and ffprobe for video/audio conversion. Download: https://www.ffmpeg.org/download.html
|
|
||||||
- audiowaveform for audio waveform rendering. Download: https://github.com/bbcrd/audiowaveform
|
|
||||||
- ghostscript for PDF import. Download: https://www.ghostscript.com/download/gsdnld.html
|
|
||||||
|
|
||||||
# Data Storage
|
|
||||||
|
|
||||||
By default, media files are uploaded to the ```storage``` folder.
|
|
||||||
The database is stored in ```database.sqlite``` by default.
|
|
||||||
|
|
||||||
# Hacking
|
|
||||||
|
|
||||||
To rebuild the frontend CSS styles:
|
|
||||||
|
|
||||||
gulp styles
|
gulp styles
|
||||||
|
|
||||||
|
# Run
|
||||||
|
|
||||||
|
export NODE_ENV=development
|
||||||
|
npm start
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
The Spacedeck logo and brand assets are registered trademarks of Spacedeck GmbH. All rights reserved.
|
Spacedeck Open is released under the GNU Affero General Public License Version 3 (GNU AGPLv3).
|
||||||
|
|
||||||
Spacedeck Open source code is released under the GNU Affero General Public License Version 3 (GNU AGPLv3).
|
|
||||||
|
|
||||||
Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media
|
Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media
|
||||||
Copyright (C) 2011-2018 Lukas F. Hartmann, Martin Güther
|
Copyright (C) 2011-2017 Lukas F. Hartmann, Martin Güther, Thomas Helbig
|
||||||
Icons and original CSS design copyright by Thomas Helbig
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
192
app.js
192
app.js
@ -1,33 +1,173 @@
|
|||||||
const spacedeck = require('./spacedeck')
|
"use strict";
|
||||||
|
|
||||||
const electron = require('electron')
|
require('./models/schema');
|
||||||
const electronApp = electron.app
|
require("log-timestamp");
|
||||||
const BrowserWindow = electron.BrowserWindow
|
|
||||||
let mainWindow
|
|
||||||
|
|
||||||
function createWindow () {
|
const config = require('config');
|
||||||
mainWindow = new BrowserWindow({width: 1200, height: 700})
|
const redis = require('./helpers/redis');
|
||||||
mainWindow.loadURL("http://localhost:9666")
|
const websockets = require('./helpers/websockets');
|
||||||
mainWindow.on('closed', function () {
|
|
||||||
mainWindow = null
|
const http = require('http');
|
||||||
})
|
const path = require('path');
|
||||||
|
|
||||||
|
const _ = require('underscore');
|
||||||
|
const favicon = require('serve-favicon');
|
||||||
|
const logger = require('morgan');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const swig = require('swig');
|
||||||
|
const i18n = require('i18n-2');
|
||||||
|
const helmet = require('helmet');
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const isProduction = app.get('env') === 'production';
|
||||||
|
|
||||||
|
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
|
||||||
|
|
||||||
|
app.use(logger(isProduction ? 'combined' : 'dev'));
|
||||||
|
|
||||||
|
i18n.expressBind(app, {
|
||||||
|
locales: ["en", "de", "fr"],
|
||||||
|
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');
|
||||||
|
|
||||||
|
if (app.get('env') != 'development') {
|
||||||
|
app.set('views', path.join(__dirname, 'build', 'views'));
|
||||||
|
app.use(favicon(path.join(__dirname, 'build', 'assets', 'images', 'favicon.png')));
|
||||||
|
app.use(express.static(path.join(__dirname, 'build', 'assets')));
|
||||||
|
} else {
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.png')));
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
}
|
}
|
||||||
|
|
||||||
electronApp.on('ready', createWindow)
|
app.use(bodyParser.json({
|
||||||
|
limit: '50mb'
|
||||||
|
}));
|
||||||
|
|
||||||
// Quit when all windows are closed.
|
app.use(bodyParser.urlencoded({
|
||||||
electronApp.on('window-all-closed', function () {
|
extended: false,
|
||||||
// On OS X it is common for applications and their menu bar
|
limit: '50mb'
|
||||||
// to stay active until the user quits explicitly with Cmd + Q
|
}));
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
electronApp.quit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
electronApp.on('activate', function () {
|
app.use(cookieParser());
|
||||||
// On OS X it's common to re-create a window in the app when the
|
app.use(helmet.noCache())
|
||||||
// dock icon is clicked and there are no other windows open.
|
app.use(helmet.frameguard())
|
||||||
if (mainWindow === null) {
|
app.use(helmet.xssFilter())
|
||||||
createWindow()
|
app.use(helmet.hsts({
|
||||||
|
maxAge: 7776000000,
|
||||||
|
includeSubdomains: true
|
||||||
|
}))
|
||||||
|
app.disable('x-powered-by');
|
||||||
|
app.use(helmet.noSniff())
|
||||||
|
|
||||||
|
// CUSTOM MIDDLEWARES
|
||||||
|
|
||||||
|
app.use(require("./middlewares/templates"));
|
||||||
|
app.use(require("./middlewares/error_helpers"));
|
||||||
|
app.use(require("./middlewares/setuser"));
|
||||||
|
app.use(require("./middlewares/subdomain"));
|
||||||
|
app.use(require("./middlewares/cors"));
|
||||||
|
app.use(require("./middlewares/i18n"));
|
||||||
|
app.use("/api", require("./middlewares/api_helpers"));
|
||||||
|
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
|
||||||
|
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
|
||||||
|
app.use('/api/teams', require("./middlewares/team_helpers"));
|
||||||
|
|
||||||
|
// REAL ROUTES
|
||||||
|
|
||||||
|
app.use('/api/users', require('./routes/api/users'));
|
||||||
|
app.use('/api/memberships', require('./routes/api/memberships'));
|
||||||
|
|
||||||
|
const spaceRouter = require('./routes/api/spaces');
|
||||||
|
app.use('/api/spaces', spaceRouter);
|
||||||
|
|
||||||
|
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
|
||||||
|
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
|
||||||
|
spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
|
||||||
|
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
|
||||||
|
spaceRouter.use('/:id', require('./routes/api/space_exports'));
|
||||||
|
|
||||||
|
app.use('/api/sessions', require('./routes/api/sessions'));
|
||||||
|
app.use('/api/teams', require('./routes/api/teams'));
|
||||||
|
app.use('/api/webgrabber', require('./routes/api/webgrabber'));
|
||||||
|
app.use('/', require('./routes/root'));
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
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'));
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
module.exports = app;
|
||||||
|
|
||||||
|
// CONNECT TO DATABASE
|
||||||
|
const mongoHost = process.env.MONGO_PORT_27017_TCP_ADDR || 'localhost';
|
||||||
|
mongoose.connect('mongodb://' + mongoHost + '/spacedeck');
|
||||||
|
|
||||||
|
// START WEBSERVER
|
||||||
|
const port = 9000;
|
||||||
|
|
||||||
|
const server = http.Server(app).listen(port, () => {
|
||||||
|
|
||||||
|
if ("send" in process) {
|
||||||
|
process.send('online');
|
||||||
|
}
|
||||||
|
|
||||||
|
}).on('listening', () => {
|
||||||
|
|
||||||
|
const host = server.address().address;
|
||||||
|
const port = server.address().port;
|
||||||
|
console.log('Spacedeck Open listening at http://%s:%s', host, port);
|
||||||
|
|
||||||
|
}).on('error', (error) => {
|
||||||
|
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//WEBSOCKETS & WORKER
|
||||||
|
websockets.startWebsockets(server);
|
||||||
|
redis.connectRedis();
|
||||||
|
|
||||||
|
process.on('message', (message) => {
|
||||||
|
console.log("Process message:", message);
|
||||||
|
if (message === 'shutdown') {
|
||||||
|
console.log("Exiting spacedeck.");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -1,34 +1,9 @@
|
|||||||
{
|
{
|
||||||
"team_name": "My Open Spacedeck",
|
"endpoint": "http://localhost:9000",
|
||||||
"contact_email": "support@example.org",
|
"storage_bucket": "my_spacedeck_s3_bucket",
|
||||||
|
"storage_cdn": "xyz.cloudfront.net",
|
||||||
"endpoint": "http://localhost:9666",
|
|
||||||
"invite_code": "", //disabled invite code by default
|
|
||||||
|
|
||||||
"storage_region": "eu-central-1",
|
|
||||||
//"storage_bucket": "sdeck-development",
|
|
||||||
//"storage_cdn": "http://localhost:9123/sdeck-development",
|
|
||||||
//"storage_endpoint": "http://storage:9000",
|
|
||||||
|
|
||||||
"storage_bucket": "my_spacedeck_bucket",
|
|
||||||
"storage_cdn": "/storage",
|
|
||||||
"storage_local_path": "./storage",
|
|
||||||
|
|
||||||
"redis_mock": true,
|
|
||||||
"mongodb_host": "localhost",
|
|
||||||
"redis_host": "localhost",
|
|
||||||
|
|
||||||
"google_access" : "",
|
"google_access" : "",
|
||||||
"google_secret" : "",
|
"google_secret" : "",
|
||||||
"admin_pass": "very_secret_admin_password",
|
"admin_pass": "very_secret_admin_password",
|
||||||
"phantom_api_secret": "very_secret_phantom_password",
|
"phantom_api_secret": "very_secret_phantom_password"
|
||||||
|
|
||||||
// Choose "console" or "smtp"
|
|
||||||
"mail_provider": "smtp",
|
|
||||||
"mail_smtp_host": "your.smtp.host",
|
|
||||||
"mail_smtp_port": 465,
|
|
||||||
"mail_smtp_secure": true,
|
|
||||||
"mail_smtp_require_tls": true,
|
|
||||||
"mail_smtp_user": "your.smtp.user",
|
|
||||||
"mail_smtp_pass": "your.secret.smtp.password"
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
# Windows Electron Build
|
|
||||||
|
|
||||||
sqlite3 needs to be manually built for the iojs version that electron ships. The following code assumes electron v1.8.4.
|
|
||||||
|
|
||||||
````
|
|
||||||
npm -g install windows-build-tools
|
|
||||||
|
|
||||||
cd node_modules\sqlite3
|
|
||||||
|
|
||||||
node-gyp configure --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64
|
|
||||||
|
|
||||||
node-gyp rebuild --target=1.8.4 --target_platform=win32 --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/electron-v1.8-win32-x64 --msvs_version=2015
|
|
||||||
|
|
||||||
cd ..\..
|
|
||||||
````
|
|
||||||
|
|
@ -4,18 +4,51 @@ const exec = require('child_process');
|
|||||||
const gm = require('gm');
|
const gm = require('gm');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const Models = require('../models/db');
|
const Models = require('../models/schema');
|
||||||
const uploader = require('../helpers/uploader');
|
const uploader = require('../helpers/uploader');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
|
||||||
|
|
||||||
const db = require('../models/db');
|
const fileExtensionMap = {
|
||||||
const Sequelize = require('sequelize');
|
".amr" : "audio/AMR",
|
||||||
const Op = Sequelize.Op;
|
".ogg" : "audio/ogg",
|
||||||
|
".aac" : "audio/aac",
|
||||||
const mime = require('mime-types');
|
".mp3" : "audio/mpeg",
|
||||||
const fileType = require('file-type');
|
".mpg" : "video/mpeg",
|
||||||
const readChunk = require('read-chunk');
|
".3ga" : "audio/3ga",
|
||||||
|
".mp4" : "video/mp4",
|
||||||
|
".wav" : "audio/wav",
|
||||||
|
".mov" : "video/quicktime",
|
||||||
|
".doc" : "application/msword",
|
||||||
|
".dot" : "application/msword",
|
||||||
|
".docx" : "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
".dotx" : "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||||
|
".docm" : "application/vnd.ms-word.document.macroEnabled.12",
|
||||||
|
".dotm" : "application/vnd.ms-word.template.macroEnabled.12",
|
||||||
|
".xls" : "application/vnd.ms-excel",
|
||||||
|
".xlt" : "application/vnd.ms-excel",
|
||||||
|
".xla" : "application/vnd.ms-excel",
|
||||||
|
".xlsx" : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
".xltx" : "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||||
|
".xlsm" : "application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||||
|
".xltm" : "application/vnd.ms-excel.template.macroEnabled.12",
|
||||||
|
".xlam" : "application/vnd.ms-excel.addin.macroEnabled.12",
|
||||||
|
".xlsb" : "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||||
|
".ppt" : "application/vnd.ms-powerpoint",
|
||||||
|
".pot" : "application/vnd.ms-powerpoint",
|
||||||
|
".pps" : "application/vnd.ms-powerpoint",
|
||||||
|
".ppa" : "application/vnd.ms-powerpoint",
|
||||||
|
".pptx" : "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
".potx" : "application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||||
|
".ppsx" : "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||||
|
".ppam" : "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
||||||
|
".pptm" : "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||||
|
".potm" : "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||||
|
".ppsm" : "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
||||||
|
".key" : "application/x-iwork-keynote-sffkey",
|
||||||
|
".pages" : "application/x-iwork-pages-sffpages",
|
||||||
|
".numbers" : "application/x-iwork-numbers-sffnumbers",
|
||||||
|
".ttf" : "application/x-font-ttf"
|
||||||
|
};
|
||||||
|
|
||||||
const convertableImageTypes = [
|
const convertableImageTypes = [
|
||||||
"image/png",
|
"image/png",
|
||||||
@ -35,9 +68,9 @@ const convertableVideoTypes = [
|
|||||||
|
|
||||||
const convertableAudioTypes = [
|
const convertableAudioTypes = [
|
||||||
"application/ogg",
|
"application/ogg",
|
||||||
"audio/amr",
|
"audio/AMR",
|
||||||
"audio/3ga",
|
"audio/3ga",
|
||||||
"audio/wave",
|
"audio/wav",
|
||||||
"audio/3gpp",
|
"audio/3gpp",
|
||||||
"audio/x-wav",
|
"audio/x-wav",
|
||||||
"audio/aiff",
|
"audio/aiff",
|
||||||
@ -92,14 +125,14 @@ function createWaveform(fileName, localFilePath, callback){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertVideo(fileName, filePath, codec, callback, progressCallback) {
|
function convertVideo(fileName, filePath, codec, callback, progress_callback) {
|
||||||
var ext = path.extname(fileName);
|
var ext = path.extname(fileName);
|
||||||
var presetMime = mime.lookup(fileName);
|
var presetMime = fileExtensionMap[ext];
|
||||||
|
|
||||||
var newExt = codec == "mp4" ? "mp4" : "ogv";
|
var newExt = codec == "mp4" ? "mp4" : "ogv";
|
||||||
var convertedPath = filePath + "." + newExt;
|
var convertedPath = filePath + "." + newExt;
|
||||||
|
|
||||||
console.log("converting", filePath, "to", convertedPath);
|
console.log("converting", filePath, "to", convertedPath, "progress_cb:",progress_callback);
|
||||||
|
|
||||||
var convertArgs = (codec == "mp4") ? [
|
var convertArgs = (codec == "mp4") ? [
|
||||||
"-i", filePath,
|
"-i", filePath,
|
||||||
@ -134,8 +167,8 @@ function convertVideo(fileName, filePath, codec, callback, progressCallback) {
|
|||||||
|
|
||||||
ff.stderr.on('data', function (data) {
|
ff.stderr.on('data', function (data) {
|
||||||
console.log('[ffmpeg-video] stderr: ' + data);
|
console.log('[ffmpeg-video] stderr: ' + data);
|
||||||
if (progressCallback) {
|
if (progress_callback) {
|
||||||
progressCallback(data);
|
progress_callback(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,7 +185,7 @@ function convertVideo(fileName, filePath, codec, callback, progressCallback) {
|
|||||||
|
|
||||||
function convertAudio(fileName, filePath, codec, callback) {
|
function convertAudio(fileName, filePath, codec, callback) {
|
||||||
var ext = path.extname(fileName);
|
var ext = path.extname(fileName);
|
||||||
var presetMime = mime.lookup(fileName);
|
var presetMime = fileExtensionMap[ext];
|
||||||
|
|
||||||
var newExt = codec == "mp3" ? "mp3" : "ogg";
|
var newExt = codec == "mp3" ? "mp3" : "ogg";
|
||||||
var convertedPath = filePath + "." + newExt;
|
var convertedPath = filePath + "." + newExt;
|
||||||
@ -189,14 +222,22 @@ function createThumbnailForVideo(fileName, filePath, callback) {
|
|||||||
|
|
||||||
function getMime(fileName, filePath, callback) {
|
function getMime(fileName, filePath, callback) {
|
||||||
var ext = path.extname(fileName);
|
var ext = path.extname(fileName);
|
||||||
var presetMime = mime.lookup(fileName);
|
var presetMime = fileExtensionMap[ext];
|
||||||
|
|
||||||
if (presetMime) {
|
if (presetMime) {
|
||||||
callback(null, presetMime);
|
callback(null, presetMime);
|
||||||
} else {
|
} else {
|
||||||
const buffer = readChunk.sync(filePath, 0, 4100);
|
exec.execFile("file", ["-b","--mime-type", filePath], {}, function(error, stdout, stderr) {
|
||||||
var mimeType = fileType(buffer);
|
console.log("file stdout: ",stdout);
|
||||||
callback(null, mimeType);
|
if (stderr === '' && error == null) {
|
||||||
|
//filter special chars from commandline
|
||||||
|
var mime = stdout.replace(/[^a-zA-Z0-9\/\-]/g,'');
|
||||||
|
callback(null, mime);
|
||||||
|
} else {
|
||||||
|
console.log("getMime file error: ", error);
|
||||||
|
callback(error, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,12 +245,13 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
|
|||||||
if (max>320 || size.width > max || size.height > max) {
|
if (max>320 || size.width > max || size.height > max) {
|
||||||
var resizedFileName = max + "_"+fileName;
|
var resizedFileName = max + "_"+fileName;
|
||||||
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + resizedFileName;
|
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + resizedFileName;
|
||||||
var localResizedFilePath = os.tmpdir()+"/"+resizedFileName;
|
var localResizedFilePath = "/tmp/"+resizedFileName;
|
||||||
gm(localFilePath).resize(max, max).autoOrient().write(localResizedFilePath, function (err) {
|
gm(localFilePath).resize(max, max).autoOrient().write(localResizedFilePath, function (err) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
uploader.uploadFile(s3Key, "image/jpeg", localResizedFilePath, function(err, url) {
|
uploader.uploadFile(s3Key, "image/jpeg", localResizedFilePath, function(err, url) {
|
||||||
if (err) callback(err);
|
if (err) callback(err);
|
||||||
else{
|
else{
|
||||||
|
console.log(localResizedFilePath);
|
||||||
fs.unlink(localResizedFilePath, function (err) {
|
fs.unlink(localResizedFilePath, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -230,7 +272,7 @@ function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) {
|
var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
small: function(callback){
|
small: function(callback){
|
||||||
resizeAndUpload(a, size, 320, fileName, imageFilePath, callback);
|
resizeAndUpload(a, size, 320, fileName, imageFilePath, callback);
|
||||||
@ -243,13 +285,13 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
|
|||||||
},
|
},
|
||||||
original: function(callback){
|
original: function(callback){
|
||||||
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id + "/" + fileNameOrg;
|
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id + "/" + fileNameOrg;
|
||||||
uploader.uploadFile(s3Key, mimeType, originalFilePath, function(err, url){
|
uploader.uploadFile(s3Key, mime, originalFilePath, function(err, url){
|
||||||
callback(null, url);
|
callback(null, url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, function(err, results) {
|
}, function(err, results) {
|
||||||
a.state = "idle";
|
a.state = "idle";
|
||||||
a.mime = mimeType;
|
a.mime = mime;
|
||||||
var stats = fs.statSync(originalFilePath);
|
var stats = fs.statSync(originalFilePath);
|
||||||
|
|
||||||
a.payload_size = stats["size"];
|
a.payload_size = stats["size"];
|
||||||
@ -259,13 +301,15 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
|
|||||||
a.payload_uri = results.original;
|
a.payload_uri = results.original;
|
||||||
|
|
||||||
var factor = 320/size.width;
|
var factor = 320/size.width;
|
||||||
a.w = Math.round(size.width*factor);
|
var newBoardSpecs = a.board;
|
||||||
a.h = Math.round(size.height*factor);
|
newBoardSpecs.w = Math.round(size.width*factor);
|
||||||
|
newBoardSpecs.h = Math.round(size.height*factor);
|
||||||
|
a.board = newBoardSpecs;
|
||||||
|
|
||||||
a.updated_at = new Date();
|
a.updated_at = new Date();
|
||||||
db.packArtifact(a);
|
a.save(function(err) {
|
||||||
|
if(err) payloadCallback(err, null);
|
||||||
a.save().then(function() {
|
else {
|
||||||
fs.unlink(originalFilePath, function (err) {
|
fs.unlink(originalFilePath, function (err) {
|
||||||
if (err){
|
if (err){
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -275,27 +319,28 @@ var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, im
|
|||||||
payloadCallback(null, a);
|
payloadCallback(null, a);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
convert: function(a, fileName, localFilePath, payloadCallback, progressCallback) {
|
convert: function(a, fileName, localFilePath, payloadCallback, progress_callback) {
|
||||||
getMime(fileName, localFilePath, function(err, mimeType){
|
getMime(fileName, localFilePath, function(err, mime){
|
||||||
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
|
console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mime:", mime);
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if (convertableImageTypes.indexOf(mimeType) > -1) {
|
if (convertableImageTypes.indexOf(mime) > -1) {
|
||||||
|
|
||||||
gm(localFilePath).size(function (err, size) {
|
gm(localFilePath).size(function (err, size) {
|
||||||
console.log("[convert] gm:", err, size);
|
console.log("[convert] gm:", err, size);
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if(mimeType == "application/pdf") {
|
if(mime == "application/pdf") {
|
||||||
var firstImagePath = localFilePath + ".jpeg";
|
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) {
|
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) {
|
if(error === null) {
|
||||||
resizeAndUploadImage(a, mimeType, size, fileName + ".jpeg", fileName, firstImagePath, localFilePath, function(err, a) {
|
resizeAndUploadImage(a, mime, size, fileName + ".jpeg", fileName, firstImagePath, localFilePath, function(err, a) {
|
||||||
fs.unlink(firstImagePath, function (err) {
|
fs.unlink(firstImagePath, function (err) {
|
||||||
payloadCallback(err, a);
|
payloadCallback(err, a);
|
||||||
});
|
});
|
||||||
@ -305,19 +350,19 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if(mimeType == "image/gif") {
|
} else if(mime == "image/gif") {
|
||||||
//gifs are buggy after convertion, so we should not convert them
|
//gifs are buggy after convertion, so we should not convert them
|
||||||
|
|
||||||
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
||||||
|
|
||||||
uploader.uploadFile(s3Key, "image/gif", localFilePath, function(err, url) {
|
uploader.uploadFile(s3Key, "image/gif", localFilePath, function(err, url) {
|
||||||
if (err) payloadCallback(err);
|
if(err)callback(err);
|
||||||
else{
|
else{
|
||||||
console.log(localFilePath);
|
console.log(localFilePath);
|
||||||
var stats = fs.statSync(localFilePath);
|
var stats = fs.statSync(localFilePath);
|
||||||
|
|
||||||
a.state = "idle";
|
a.state = "idle";
|
||||||
a.mime = mimeType;
|
a.mime = mime;
|
||||||
|
|
||||||
a.payload_size = stats["size"];
|
a.payload_size = stats["size"];
|
||||||
a.payload_thumbnail_web_uri = url;
|
a.payload_thumbnail_web_uri = url;
|
||||||
@ -326,13 +371,15 @@ module.exports = {
|
|||||||
a.payload_uri = url;
|
a.payload_uri = url;
|
||||||
|
|
||||||
var factor = 320/size.width;
|
var factor = 320/size.width;
|
||||||
a.w = Math.round(size.width*factor);
|
var newBoardSpecs = a.board;
|
||||||
a.h = Math.round(size.height*factor);
|
newBoardSpecs.w = Math.round(size.width*factor);
|
||||||
|
newBoardSpecs.h = Math.round(size.height*factor);
|
||||||
|
a.board = newBoardSpecs;
|
||||||
|
|
||||||
a.updated_at = new Date();
|
a.updated_at = new Date();
|
||||||
db.packArtifact(a);
|
a.save(function(err){
|
||||||
|
if(err) payloadCallback(err, null);
|
||||||
a.save().then(function() {
|
else {
|
||||||
fs.unlink(localFilePath, function (err) {
|
fs.unlink(localFilePath, function (err) {
|
||||||
if (err){
|
if (err){
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -342,17 +389,18 @@ module.exports = {
|
|||||||
payloadCallback(null, a);
|
payloadCallback(null, a);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
resizeAndUploadImage(a, mimeType, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
|
resizeAndUploadImage(a, mime, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
|
||||||
}
|
}
|
||||||
} else payloadCallback(err);
|
} else payloadCallback(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (convertableVideoTypes.indexOf(mimeType) > -1) {
|
} else if (convertableVideoTypes.indexOf(mime) > -1) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
thumbnail: function(callback) {
|
thumbnail: function(callback) {
|
||||||
createThumbnailForVideo(fileName, localFilePath, function(err, created){
|
createThumbnailForVideo(fileName, localFilePath, function(err, created){
|
||||||
@ -368,7 +416,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
ogg: function(callback) {
|
ogg: function(callback) {
|
||||||
if (mimeType == "video/ogg") {
|
if (mime == "video/ogg") {
|
||||||
callback(null, "org");
|
callback(null, "org");
|
||||||
} else {
|
} else {
|
||||||
convertVideo(fileName, localFilePath, "ogg", function(err, file) {
|
convertVideo(fileName, localFilePath, "ogg", function(err, file) {
|
||||||
@ -380,11 +428,11 @@ module.exports = {
|
|||||||
else callback(null, url);
|
else callback(null, url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, progressCallback);
|
}, progress_callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mp4: function(callback) {
|
mp4: function(callback) {
|
||||||
if (mimeType == "video/mp4") {
|
if (mime == "video/mp4") {
|
||||||
callback(null, "org");
|
callback(null, "org");
|
||||||
} else {
|
} else {
|
||||||
convertVideo(fileName, localFilePath, "mp4", function(err, file) {
|
convertVideo(fileName, localFilePath, "mp4", function(err, file) {
|
||||||
@ -396,11 +444,11 @@ module.exports = {
|
|||||||
else callback(null, url);
|
else callback(null, url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, progressCallback);
|
}, progress_callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
original: function(callback){
|
original: function(callback){
|
||||||
uploader.uploadFile(fileName, mimeType, localFilePath, function(err, url){
|
uploader.uploadFile(fileName, mime, localFilePath, function(err, url){
|
||||||
callback(null, url);
|
callback(null, url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -410,7 +458,7 @@ module.exports = {
|
|||||||
if (err) payloadCallback(err, a);
|
if (err) payloadCallback(err, a);
|
||||||
else {
|
else {
|
||||||
a.state = "idle";
|
a.state = "idle";
|
||||||
a.mime = mimeType;
|
a.mime = mime;
|
||||||
var stats = fs.statSync(localFilePath);
|
var stats = fs.statSync(localFilePath);
|
||||||
|
|
||||||
a.payload_size = stats["size"];
|
a.payload_size = stats["size"];
|
||||||
@ -419,7 +467,7 @@ module.exports = {
|
|||||||
a.payload_thumbnail_big_uri = results.thumbnail;
|
a.payload_thumbnail_big_uri = results.thumbnail;
|
||||||
a.payload_uri = results.original;
|
a.payload_uri = results.original;
|
||||||
|
|
||||||
if (mimeType == "video/mp4") {
|
if (mime == "video/mp4") {
|
||||||
a.payload_alternatives = [
|
a.payload_alternatives = [
|
||||||
{
|
{
|
||||||
mime: "video/ogg",
|
mime: "video/ogg",
|
||||||
@ -435,10 +483,10 @@ module.exports = {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
db.packArtifact(a);
|
|
||||||
|
|
||||||
a.updated_at = new Date();
|
a.updated_at = new Date();
|
||||||
a.save().then(function() {
|
a.save(function(err) {
|
||||||
|
if (err) payloadCallback(err, null);
|
||||||
|
else {
|
||||||
fs.unlink(localFilePath, function (err) {
|
fs.unlink(localFilePath, function (err) {
|
||||||
if (err){
|
if (err){
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -448,11 +496,12 @@ module.exports = {
|
|||||||
payloadCallback(null, a);
|
payloadCallback(null, a);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (convertableAudioTypes.indexOf(mimeType) > -1) {
|
} else if (convertableAudioTypes.indexOf(mime) > -1) {
|
||||||
|
|
||||||
async.parallel({
|
async.parallel({
|
||||||
ogg: function(callback) {
|
ogg: function(callback) {
|
||||||
@ -490,7 +539,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
original: function(callback) {
|
original: function(callback) {
|
||||||
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
||||||
uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url){
|
uploader.uploadFile(keyName, mime, localFilePath, function(err, url){
|
||||||
callback(null, url);
|
callback(null, url);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -501,7 +550,7 @@ module.exports = {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
a.state = "idle";
|
a.state = "idle";
|
||||||
a.mime = mimeType;
|
a.mime = mime;
|
||||||
var stats = fs.statSync(localFilePath);
|
var stats = fs.statSync(localFilePath);
|
||||||
|
|
||||||
a.payload_size = stats["size"];
|
a.payload_size = stats["size"];
|
||||||
@ -515,10 +564,9 @@ module.exports = {
|
|||||||
];
|
];
|
||||||
|
|
||||||
a.updated_at = new Date();
|
a.updated_at = new Date();
|
||||||
|
a.save(function(err){
|
||||||
db.packArtifact(a);
|
if(err) payloadCallback(err, null);
|
||||||
|
else {
|
||||||
a.save().then(function(){
|
|
||||||
fs.unlink(localFilePath, function (err) {
|
fs.unlink(localFilePath, function (err) {
|
||||||
if (err){
|
if (err){
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -528,27 +576,31 @@ module.exports = {
|
|||||||
payloadCallback(null, a);
|
payloadCallback(null, a);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log("mimeType not matched for conversion, storing file");
|
console.log("mime not matched for conversion, storing file");
|
||||||
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
|
||||||
uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url) {
|
uploader.uploadFile(keyName, mime, localFilePath, function(err, url) {
|
||||||
|
|
||||||
a.state = "idle";
|
a.state = "idle";
|
||||||
a.mime = mimeType;
|
a.mime = mime;
|
||||||
var stats = fs.statSync(localFilePath);
|
var stats = fs.statSync(localFilePath);
|
||||||
a.payload_size = stats["size"];
|
a.payload_size = stats["size"];
|
||||||
a.payload_uri = url;
|
a.payload_uri = url;
|
||||||
|
|
||||||
a.updated_at = new Date();
|
a.updated_at = new Date();
|
||||||
a.save().then(function() {
|
a.save(function(err) {
|
||||||
|
if(err) payloadCallback(err, null);
|
||||||
|
else {
|
||||||
fs.unlink(localFilePath, function (err) {
|
fs.unlink(localFilePath, function (err) {
|
||||||
payloadCallback(null, a);
|
payloadCallback(null, a);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const extract = require('extract-zip')
|
|
||||||
const config = require('config')
|
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const db = require('../models/db')
|
|
||||||
const Sequelize = require('sequelize')
|
|
||||||
const Op = Sequelize.Op
|
|
||||||
const uuidv4 = require('uuid/v4')
|
|
||||||
|
|
||||||
require('../models/db')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
importZIP: function(user, zipPath) {
|
|
||||||
|
|
||||||
// 1. extract zip to local storage folder
|
|
||||||
// 2. read spaces.json from this folder
|
|
||||||
// 3. iterate through spaces and read all their artifact jsons
|
|
||||||
// 4. fixup storage paths
|
|
||||||
// 5. replace creator id by user._id
|
|
||||||
|
|
||||||
let relativeImportDir = 'import_'+user._id
|
|
||||||
let importDir = path.resolve(config.get('storage_local_path')+'/'+config.get('storage_bucket')+'/'+relativeImportDir)
|
|
||||||
|
|
||||||
if (!fs.existsSync(importDir)) {
|
|
||||||
fs.mkdirSync(importDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
extract(zipPath, {dir: importDir}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
console.log(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log('[import] extracted to',importDir)
|
|
||||||
|
|
||||||
let spacesJson = fs.readFileSync(importDir+'/spaces.json')
|
|
||||||
let spaces = JSON.parse(spacesJson)
|
|
||||||
var homeFolderId = null
|
|
||||||
|
|
||||||
console.log('[import] spaces:',spaces.length)
|
|
||||||
|
|
||||||
// pass 1: find homefolder
|
|
||||||
for (var i=0; i<spaces.length; i++) {
|
|
||||||
let space = spaces[i]
|
|
||||||
if (!space.parent_space_id) {
|
|
||||||
homeFolderId = space._id
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[import] homeFolderId:",homeFolderId)
|
|
||||||
|
|
||||||
for (var i=0; i<spaces.length; i++) {
|
|
||||||
let space = spaces[i]
|
|
||||||
|
|
||||||
if (space.parent_space_id) {
|
|
||||||
let artifacts = JSON.parse(fs.readFileSync(importDir+'/'+space._id+'_artifacts.json'))
|
|
||||||
console.log('[import] space',space._id,'artifacts:',artifacts.length)
|
|
||||||
|
|
||||||
//let q = {where: {_id: space._id}}
|
|
||||||
space.creator_id = user._id
|
|
||||||
delete space.__v
|
|
||||||
|
|
||||||
// transplant homefolder
|
|
||||||
console.log("parent:",space.parent_space_id)
|
|
||||||
if (space.parent_space_id+"" == homeFolderId+"") {
|
|
||||||
space.parent_space_id = user.home_folder_id
|
|
||||||
}
|
|
||||||
|
|
||||||
// move nested attrs
|
|
||||||
console.log(space)
|
|
||||||
for (k in space.advanced) {
|
|
||||||
space[k] = space.advanced[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Space.create(space)
|
|
||||||
.error((err) => {
|
|
||||||
console.error("[import] space upsert err:",err)
|
|
||||||
})
|
|
||||||
|
|
||||||
for (var j=0; j<artifacts.length; j++) {
|
|
||||||
let a = artifacts[j]
|
|
||||||
|
|
||||||
let q = {_id: a._id}
|
|
||||||
a.user_id = user._id
|
|
||||||
delete a.__v
|
|
||||||
delete a.payload_thumbnail_big_uri
|
|
||||||
|
|
||||||
// move nested attrs
|
|
||||||
for (k in a.style) {
|
|
||||||
a[k] = a.style[k]
|
|
||||||
}
|
|
||||||
for (k in a.meta) {
|
|
||||||
a[k] = a.meta[k]
|
|
||||||
}
|
|
||||||
for (k in a.board) {
|
|
||||||
a[k] = a.board[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefix = "/storage/"+relativeImportDir+"/"+space._id+"_files/"
|
|
||||||
if (a.thumbnail_uri && a.thumbnail_uri[0]!='/') a.thumbnail_uri = prefix + a.thumbnail_uri
|
|
||||||
if (a.payload_uri && a.payload_uri[0]!='/') a.payload_uri = prefix + a.payload_uri
|
|
||||||
if (a.payload_thumbnail_web_uri && a.payload_thumbnail_web_uri[0]!='/') a.payload_thumbnail_web_uri = prefix + a.payload_thumbnail_web_uri
|
|
||||||
if (a.payload_thumbnail_medium_uri && a.payload_thumbnail_medium_uri[0]!='/') a.payload_thumbnail_medium_uri = prefix + a.payload_thumbnail_medium_uri
|
|
||||||
|
|
||||||
if (a.payload_alternatives) {
|
|
||||||
for (var k=0; k<a.payload_alternatives.length; k++) {
|
|
||||||
let alt = a.payload_alternatives[k]
|
|
||||||
|
|
||||||
if (alt.payload_uri && alt.payload_uri[0]!='/') alt.payload_uri = prefix + alt.payload_uri
|
|
||||||
if (alt.payload_thumbnail_web_uri && alt.payload_thumbnail_web_uri[0]!='/') alt.payload_thumbnail_web_uri = prefix + alt.payload_thumbnail_web_uri
|
|
||||||
if (alt.payload_thumbnail_medium_uri && alt.payload_thumbnail_medium_uri[0]!='/') alt.payload_thumbnail_medium_uri = prefix + alt.payload_thumbnail_medium_uri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.packArtifact(a)
|
|
||||||
|
|
||||||
db.Artifact.create(a).error(function(err) {
|
|
||||||
console.error("[import] artifact upsert err:",err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('config');
|
var swig = require('swig');
|
||||||
const nodemailer = require('nodemailer');
|
var AWS = require('aws-sdk');
|
||||||
const swig = require('swig');
|
|
||||||
//var AWS = require('aws-sdk');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sendMail: (to_email, subject, body, options) => {
|
sendMail: (to_email, subject, body, options) => {
|
||||||
|
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const teamname = options.teamname || config.get('team_name');
|
// FIXME
|
||||||
const from = teamname + ' <' + config.get('contact_email') + '>';
|
const teamname = options.teamname || "My Open Spacedeck"
|
||||||
|
const from = teamname + ' <support@example.org>';
|
||||||
|
|
||||||
let reply_to = [from];
|
let reply_to = [from];
|
||||||
if (options.reply_to) {
|
if (options.reply_to) {
|
||||||
@ -29,38 +29,33 @@ module.exports = {
|
|||||||
options: options
|
options: options
|
||||||
});
|
});
|
||||||
|
|
||||||
if (config.get('mail_provider') === 'console') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
|
||||||
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" + htmlText + "\n\n plaintext:\n" + plaintext);
|
||||||
|
|
||||||
} else if (config.get('mail_provider') === 'smtp') {
|
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
|
||||||
host: config.get('mail_smtp_host'),
|
|
||||||
port: config.get('mail_smtp_port'),
|
|
||||||
secure: config.get('mail_smtp_secure'),
|
|
||||||
requireTLS: config.get('mail_smtp_require_tls'),
|
|
||||||
auth: {
|
|
||||||
user: config.get('mail_smtp_user'),
|
|
||||||
pass: config.get('mail_smtp_pass'),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
transporter.sendMail({
|
|
||||||
from: from,
|
|
||||||
replyTo: reply_to,
|
|
||||||
to: to_email,
|
|
||||||
subject: subject,
|
|
||||||
text: plaintext,
|
|
||||||
html: htmlText,
|
|
||||||
}, function(err, info) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Error sending email:", err);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Email sent.");
|
AWS.config.update({region: 'eu-west-1'});
|
||||||
}
|
var ses = new AWS.SES();
|
||||||
});
|
|
||||||
|
|
||||||
|
ses.sendEmail( {
|
||||||
|
Source: from,
|
||||||
|
Destination: { ToAddresses: [to_email] },
|
||||||
|
ReplyToAddresses: reply_to,
|
||||||
|
Message: {
|
||||||
|
Subject: {
|
||||||
|
Data: subject
|
||||||
|
},
|
||||||
|
Body: {
|
||||||
|
Text: {
|
||||||
|
Data: plaintext,
|
||||||
|
},
|
||||||
|
Html: {
|
||||||
|
Data: htmlText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(err, data) {
|
||||||
|
if(err) console.log('Email not sent:', err);
|
||||||
|
else console.log("Email sent.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const db = require('../models/db');
|
require('../models/schema');
|
||||||
const config = require('config');
|
var config = require('config');
|
||||||
const phantom = require('node-phantom-simple');
|
var phantom = require('node-phantom-simple');
|
||||||
const os = require('os');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// type = "pdf" or "png"
|
// type = "pdf" or "png"
|
||||||
@ -11,7 +10,7 @@ module.exports = {
|
|||||||
var spaceId = space._id;
|
var spaceId = space._id;
|
||||||
var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html";
|
var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html";
|
||||||
|
|
||||||
var export_path = os.tmpdir()+"/"+spaceId+"."+type;
|
var export_path = "/tmp/"+spaceId+"."+type;
|
||||||
|
|
||||||
var timeout = 5000;
|
var timeout = 5000;
|
||||||
if (type=="pdf") timeout = 30000;
|
if (type=="pdf") timeout = 30000;
|
||||||
@ -25,7 +24,7 @@ module.exports = {
|
|||||||
|
|
||||||
var on_exit = function(exit_code) {
|
var on_exit = function(exit_code) {
|
||||||
if (exit_code>0) {
|
if (exit_code>0) {
|
||||||
console.error("phantom abnormal exit for url "+space_url);
|
console.log("phantom abnormal exit for url "+space_url);
|
||||||
if (!on_success_called && on_error) {
|
if (!on_success_called && on_error) {
|
||||||
on_error();
|
on_error();
|
||||||
}
|
}
|
||||||
@ -33,16 +32,13 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
|
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
} else {
|
|
||||||
return browser.createPage(function (err, page) {
|
return browser.createPage(function (err, page) {
|
||||||
console.log("page created, opening ",space_url);
|
console.log("page created, opening ",space_url);
|
||||||
|
|
||||||
if (type=="pdf") {
|
if (type=="pdf") {
|
||||||
var psz = {
|
var psz = {
|
||||||
width: space.width+"px",
|
width: space.advanced.width+"px",
|
||||||
height: space.height+"px"
|
height: space.advanced.height+"px"
|
||||||
};
|
};
|
||||||
page.set('paperSize', psz);
|
page.set('paperSize', psz);
|
||||||
}
|
}
|
||||||
@ -61,8 +57,6 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
}, {
|
}, {
|
||||||
onExit: on_exit
|
onExit: on_exit
|
||||||
});
|
});
|
||||||
|
116
helpers/redis.js
116
helpers/redis.js
@ -1,111 +1,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('config');
|
const RedisConnection = require('ioredis');
|
||||||
|
const websockets = require('./websockets');
|
||||||
// this is a mock version of the Redis API,
|
|
||||||
// emulating Redis if it is not available locally
|
|
||||||
var notRedis = {
|
|
||||||
state: {},
|
|
||||||
topics: {},
|
|
||||||
|
|
||||||
publish: function(topic, msg, cb) {
|
|
||||||
if (!this.topics[topic]) {
|
|
||||||
this.topics[topic] = {
|
|
||||||
subscribers: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var t=this.topics[topic];
|
|
||||||
for (var i=0; i<t.subscribers.length; i++) {
|
|
||||||
var s=t.subscribers[i];
|
|
||||||
if (s.handler) {
|
|
||||||
s.handler(topic, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cb) cb(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
subscribe: function(topics, cb) {
|
|
||||||
var handle = {
|
|
||||||
handler: null,
|
|
||||||
on: function(evt, cb) {
|
|
||||||
if (evt == "message") {
|
|
||||||
this.handler = cb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i=0; i<topics.length; i++) {
|
|
||||||
var topic = topics[i];
|
|
||||||
if (!this.topics[topic]) {
|
|
||||||
this.topics[topic] = {
|
|
||||||
subscribers: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var t=this.topics[topic];
|
|
||||||
t.subscribers.push(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, topics.length);
|
|
||||||
return handle;
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function(key, cb) {
|
|
||||||
cb(null, this.state[key]);
|
|
||||||
return this.state[key];
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function(key, val, cb) {
|
|
||||||
this.state[key] = val;
|
|
||||||
cb();
|
|
||||||
},
|
|
||||||
|
|
||||||
del: function(key, cb) {
|
|
||||||
delete this.state[key];
|
|
||||||
cb(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
sadd: function(key, skey, cb) {
|
|
||||||
if (!this.state[key]) this.state[key] = {};
|
|
||||||
this.state[key][skey] = true;
|
|
||||||
cb(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
srem: function(key, skey, cb) {
|
|
||||||
if (this.state[key]) {
|
|
||||||
delete this.state[key][skey];
|
|
||||||
}
|
|
||||||
cb(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
smembers: function(key, cb) {
|
|
||||||
cb(null, Object.keys(this.state[key]));
|
|
||||||
},
|
|
||||||
|
|
||||||
incr: function(key, cb) {
|
|
||||||
if (!this.state[key]) this.state[key] = 0;
|
|
||||||
this.state[key]++;
|
|
||||||
cb(null, this.state[key]);
|
|
||||||
},
|
|
||||||
|
|
||||||
expire: function() {
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
connectRedis: function() {
|
connectRedis(){
|
||||||
if (config.get("redis_mock")) {
|
const redisHost = process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost';
|
||||||
this.connection = notRedis;
|
|
||||||
} else {
|
|
||||||
const redisHost = process.env.REDIS_PORT_6379_TCP_ADDR || 'sync';
|
|
||||||
this.connection = new RedisConnection(6379, redisHost);
|
this.connection = new RedisConnection(6379, redisHost);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getConnection: function() {
|
sendMessage(action, model, attributes, channelId) {
|
||||||
this.connectRedis();
|
|
||||||
return this.connection;
|
|
||||||
},
|
|
||||||
sendMessage: function(action, model, attributes, channelId) {
|
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
action: action,
|
action: action,
|
||||||
@ -114,12 +17,12 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
this.connection.publish('updates', data);
|
this.connection.publish('updates', data);
|
||||||
},
|
},
|
||||||
logIp: function(ip, cb) {
|
logIp(ip, cb) {
|
||||||
this.connection.incr("ip_"+ ip, (err, socketCounter) => {
|
this.connection.incr("ip_"+ ip, (err, socketCounter) => {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
rateLimit: function(namespace, ip, cb) {
|
rateLimit(namespace, ip, cb) {
|
||||||
const key = "limit_"+ namespace + "_"+ ip;
|
const key = "limit_"+ namespace + "_"+ ip;
|
||||||
const redis = this.connection;
|
const redis = this.connection;
|
||||||
|
|
||||||
@ -144,7 +47,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isOnlineInSpace: function(user, space, cb) {
|
isOnlineInSpace(user, space, cb) {
|
||||||
this.connection.smembers("space_" + space._id.toString(), function(err, list) {
|
this.connection.smembers("space_" + space._id.toString(), function(err, list) {
|
||||||
if (err) cb(err);
|
if (err) cb(err);
|
||||||
else {
|
else {
|
||||||
@ -156,6 +59,3 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
|
|
||||||
|
@ -1,41 +1,16 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var AWS = require('aws-sdk');
|
||||||
|
AWS.config.region = 'eu-central-1';
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
var s3 = null;
|
|
||||||
|
|
||||||
// use AWS S3 or local folder depending on config
|
|
||||||
if (config.get("storage_local_path")) {
|
|
||||||
var AWS = require('mock-aws-s3');
|
|
||||||
AWS.config.basePath = config.get("storage_local_path");
|
|
||||||
s3 = new AWS.S3();
|
|
||||||
} else {
|
|
||||||
var AWS = require('aws-sdk');
|
|
||||||
var storage_endpoint = config.get("storage_endpoint");
|
|
||||||
const ep = new AWS.Endpoint(storage_endpoint);
|
|
||||||
|
|
||||||
AWS.config.update(new AWS.Config({
|
|
||||||
accessKeyId: process.env.MINIO_ACCESS_KEY,
|
|
||||||
secretAccessKey: process.env.MINIO_SECRET_KEY,
|
|
||||||
region: config.get("storage_region"),
|
|
||||||
s3ForcePathStyle: true,
|
|
||||||
signatureVersion: 'v4'
|
|
||||||
}));
|
|
||||||
s3 = new AWS.S3({
|
|
||||||
endpoint: ep
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
s3.createBucket({
|
|
||||||
Bucket: config.get("storage_bucket"),
|
|
||||||
ACL: "public-read",
|
|
||||||
GrantRead: "*"
|
|
||||||
}, (err,res) => {
|
|
||||||
console.log("createBucket",err,res);
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
removeFile: (path, callback) => {
|
removeFile: (path, callback) => {
|
||||||
|
const s3 = new AWS.S3({
|
||||||
|
region: 'eu-central-1'
|
||||||
|
});
|
||||||
const bucket = config.get("storage_bucket");
|
const bucket = config.get("storage_bucket");
|
||||||
s3.deleteObject({
|
s3.deleteObject({
|
||||||
Bucket: bucket, Key: path
|
Bucket: bucket, Key: path
|
||||||
@ -53,7 +28,7 @@ module.exports = {
|
|||||||
callback({error:"missing path"}, null);
|
callback({error:"missing path"}, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("[storage] uploading", localFilePath, " to ", fileName);
|
console.log("[s3] uploading", localFilePath, " to ", fileName);
|
||||||
|
|
||||||
const bucket = config.get("storage_bucket");
|
const bucket = config.get("storage_bucket");
|
||||||
const fileStream = fs.createReadStream(localFilePath);
|
const fileStream = fs.createReadStream(localFilePath);
|
||||||
@ -64,6 +39,11 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
fileStream.on('open', function () {
|
fileStream.on('open', function () {
|
||||||
|
// FIXME
|
||||||
|
var s3 = new AWS.S3({
|
||||||
|
region: 'eu-central-1'
|
||||||
|
});
|
||||||
|
|
||||||
s3.putObject({
|
s3.putObject({
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Key: fileName,
|
Key: fileName,
|
||||||
@ -74,7 +54,7 @@ module.exports = {
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
callback(err);
|
callback(err);
|
||||||
}else {
|
}else {
|
||||||
const url = config.get("storage_cdn") + "/" + fileName;
|
const url = "https://"+ config.get("storage_cdn") + "/" + fileName;
|
||||||
console.log("[s3]" + localFilePath + " to " + url);
|
console.log("[s3]" + localFilePath + " to " + url);
|
||||||
callback(null, url);
|
callback(null, url);
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
require('../models/schema');
|
||||||
const db = require('../models/db');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
|
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
const WebSocketServer = require('ws').Server;
|
const WebSocketServer = require('ws').Server;
|
||||||
|
|
||||||
//const RedisConnection = require('ioredis');
|
const Redis = require('ioredis');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const _ = require("underscore");
|
const _ = require("underscore");
|
||||||
|
const mongoose = require("mongoose");
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
|
||||||
const redisMock = require("./redis.js");
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
startWebsockets: function(server){
|
startWebsockets: function(server){
|
||||||
this.setupSubscription();
|
this.setupSubscription();
|
||||||
|
this.state = new Redis(6379, process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost');
|
||||||
|
|
||||||
if(!this.current_websockets){
|
if(!this.current_websockets){
|
||||||
if (config.get("redis_mock")) {
|
|
||||||
this.state = redisMock.getConnection();
|
|
||||||
} else {
|
|
||||||
this.state = new RedisConnection(6379, process.env.REDIS_PORT_6379_TCP_ADDR || config.get("redis_host"));
|
|
||||||
}
|
|
||||||
this.current_websockets = [];
|
this.current_websockets = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +37,11 @@ module.exports = {
|
|||||||
const editorAuth = msg.editor_auth;
|
const editorAuth = msg.editor_auth;
|
||||||
const spaceId = msg.space_id;
|
const spaceId = msg.space_id;
|
||||||
|
|
||||||
db.Space.findOne({where: {"_id": spaceId}}).then(space => {
|
Space.findOne({"_id": spaceId}).populate('creator').exec((err, space) => {
|
||||||
if (space) {
|
if (space) {
|
||||||
const upgradeSocket = function() {
|
const upgradeSocket = function() {
|
||||||
if (token) {
|
if (token) {
|
||||||
db.findUserBySessionToken(token, function(err, user) {
|
User.findBySessionToken(token, function(err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err, user);
|
console.error(err, user);
|
||||||
} else {
|
} else {
|
||||||
@ -127,17 +117,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setupSubscription: function() {
|
setupSubscription: function() {
|
||||||
if (config.get("redis_mock")) {
|
this.cursorSubscriber = new Redis(6379, process.env.REDIS_PORT_6379_TCP_ADDR || 'localhost');
|
||||||
this.cursorSubscriber = redisMock.getConnection().subscribe(['cursors', 'users', 'updates'], function (err, count) {
|
|
||||||
console.log("[redis-mock] websockets subscribed to " + count + " topics." );
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.cursorSubscriber = new RedisConnection(6379, process.env.REDIS_PORT_6379_TCP_ADDR || config.get("redis_host"));
|
|
||||||
this.cursorSubscriber.subscribe(['cursors', 'users', 'updates'], function (err, count) {
|
this.cursorSubscriber.subscribe(['cursors', 'users', 'updates'], function (err, count) {
|
||||||
console.log("[redis] websockets subscribed to " + count + " topics." );
|
console.log("[redis] websockets to " + count + " topics." );
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.cursorSubscriber.on('message', function (channel, rawMessage) {
|
this.cursorSubscriber.on('message', function (channel, rawMessage) {
|
||||||
const msg = JSON.parse(rawMessage);
|
const msg = JSON.parse(rawMessage);
|
||||||
const spaceId = msg.space_id;
|
const spaceId = msg.space_id;
|
||||||
@ -223,7 +206,7 @@ module.exports = {
|
|||||||
console.log("websocket not found to remove");
|
console.log("websocket not found to remove");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.del(ws.id+"", function(err, res) {
|
this.state.del(ws.id, function(err, res) {
|
||||||
if (err) console.error(err, res);
|
if (err) console.error(err, res);
|
||||||
else {
|
else {
|
||||||
this.removeUserInSpace(ws.space_id, ws, (err) => {
|
this.removeUserInSpace(ws.space_id, ws, (err) => {
|
||||||
@ -238,8 +221,7 @@ module.exports = {
|
|||||||
|
|
||||||
addUserInSpace: function(username, space, ws, cb) {
|
addUserInSpace: function(username, space, ws, cb) {
|
||||||
console.log("[websockets] user "+username+" in "+space.access_mode +" space " + space._id + " with socket " + ws.id);
|
console.log("[websockets] user "+username+" in "+space.access_mode +" space " + space._id + " with socket " + ws.id);
|
||||||
|
this.state.set(ws.id, username, function(err, res) {
|
||||||
this.state.set(ws.id+"", username+"", function(err, res) {
|
|
||||||
if(err) console.error(err, res);
|
if(err) console.error(err, res);
|
||||||
else {
|
else {
|
||||||
this.state.sadd("space_" + space._id, ws.id, function(err, res) {
|
this.state.sadd("space_" + space._id, ws.id, function(err, res) {
|
||||||
@ -256,7 +238,7 @@ module.exports = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
removeUserInSpace: function(spaceId, ws, cb) {
|
removeUserInSpace: function(spaceId, ws, cb) {
|
||||||
this.state.srem("space_" + spaceId, ws.id+"", function(err, res) {
|
this.state.srem("space_" + spaceId, ws.id, function(err, res) {
|
||||||
if (err) cb(err);
|
if (err) cb(err);
|
||||||
else {
|
else {
|
||||||
console.log("[websockets] socket "+ ws.id + " went offline in space " + spaceId);
|
console.log("[websockets] socket "+ ws.id + " went offline in space " + spaceId);
|
||||||
@ -273,7 +255,7 @@ module.exports = {
|
|||||||
if(!spaceId)
|
if(!spaceId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*this.state.smembers("space_" + spaceId, function(err, list) {
|
this.state.smembers("space_" + spaceId, function(err, list) {
|
||||||
async.map(list, function(item, callback) {
|
async.map(list, function(item, callback) {
|
||||||
this.state.get(item, function(err, userId) {
|
this.state.get(item, function(err, userId) {
|
||||||
console.log(item, "->", userId);
|
console.log(item, "->", userId);
|
||||||
@ -294,14 +276,16 @@ module.exports = {
|
|||||||
return {nickname: realNickname, email: null, avatar_thumbnail_uri: null };
|
return {nickname: realNickname, email: null, avatar_thumbnail_uri: null };
|
||||||
});
|
});
|
||||||
|
|
||||||
db.User.findAll({where: {
|
User.find({"_id" : { "$in" : validUserIds }}, { "nickname" : 1 , "email" : 1, "avatar_thumbnail_uri": 1 }, function(err, users) {
|
||||||
"_id" : { "$in" : validUserIds }}, attributes: ["nickname","email","avatar_thumbnail_uri"]})
|
if (err)
|
||||||
.then(users) {
|
console.error(err);
|
||||||
|
else {
|
||||||
const allUsers = users.concat(anonymousUsers);
|
const allUsers = users.concat(anonymousUsers);
|
||||||
const strUsers = JSON.stringify({users: allUsers, space_id: spaceId});
|
const strUsers = JSON.stringify({users: allUsers, space_id: spaceId});
|
||||||
this.state.publish("users", strUsers);
|
this.state.publish("users", strUsers);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this));*/
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
"specify": "Bitte spezifiziere",
|
"specify": "Bitte spezifiziere",
|
||||||
"confirm": "Bitte bestätige",
|
"confirm": "Bitte bestätige",
|
||||||
"signup_google": "Mit Google anmelden",
|
"signup_google": "Mit Google anmelden",
|
||||||
"error_unknown_email": "Unbekannte Kombination von Email und Passwort.",
|
"error_unknown_email": "Unbekannte Kombination von Email und Passwort. Oder versuche dich mit Google anzumelden.",
|
||||||
"error_password_confirmation": "Die beiden Passwörter stimmen nicht überein.",
|
"error_password_confirmation": "Die beiden Passwörter stimmen nicht überein.",
|
||||||
"error_domain_blocked": "Diese Domain ist gesperrt.",
|
"error_domain_blocked": "Diese Domain ist gesperrt.",
|
||||||
"error_user_email_already_used": "Diese Email-Adresse ist bereits registriert.",
|
"error_user_email_already_used": "Diese Email-Adresse ist bereits registriert.",
|
||||||
|
@ -44,7 +44,8 @@
|
|||||||
"sure": "Are you sure?",
|
"sure": "Are you sure?",
|
||||||
"specify": "Please Specify",
|
"specify": "Please Specify",
|
||||||
"confirm": "Please Confirm",
|
"confirm": "Please Confirm",
|
||||||
"error_unknown_email": "This email/password combination is unknown.",
|
"signup_google": "Sign In with Google",
|
||||||
|
"error_unknown_email": "This email/password combination is unknown. Try login with Google.",
|
||||||
"error_password_confirmation": "The entered passwords don't match.",
|
"error_password_confirmation": "The entered passwords don't match.",
|
||||||
"error_domain_blocked": "Your domain is blocked.",
|
"error_domain_blocked": "Your domain is blocked.",
|
||||||
"error_user_email_already_used": "This email address is already in use.",
|
"error_user_email_already_used": "This email address is already in use.",
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
"specify": "Veuillez préciser:",
|
"specify": "Veuillez préciser:",
|
||||||
"confirm": "Veuillez confirmer",
|
"confirm": "Veuillez confirmer",
|
||||||
"signup_google": "S'inscrire avec Google",
|
"signup_google": "S'inscrire avec Google",
|
||||||
"error_unknown_email": "Combinaison inconnue de l'email et mot de passe.",
|
"error_unknown_email": "Combinaison inconnue de l'email et mot de passe. Ou essayer de signer avec Google.",
|
||||||
"error_password_confirmation": "Les deux mots de passe ne correspondent pas.",
|
"error_password_confirmation": "Les deux mots de passe ne correspondent pas.",
|
||||||
"error_domain_blocked": "Ce domaine a été désactivé.",
|
"error_domain_blocked": "Ce domaine a été désactivé.",
|
||||||
"error_user_email_already_used": "Cette adresse email est déjà enregistré.",
|
"error_user_email_already_used": "Cette adresse email est déjà enregistré.",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('../models/db');
|
require('../models/schema');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
module.exports = (req, res, next) => {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('../models/db');
|
require('../models/schema');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const redis = require('../helpers/redis');
|
const redis = require('../helpers/redis');
|
||||||
|
|
||||||
// FIXME TODO object.toJSON()
|
|
||||||
|
|
||||||
var saveAction = (actionKey, object) => {
|
var saveAction = (actionKey, object) => {
|
||||||
if (object.constructor.modelName == "Space")
|
if (object.constructor.modelName == "Space")
|
||||||
return;
|
return;
|
||||||
@ -15,14 +13,14 @@ var saveAction = (actionKey, object) => {
|
|||||||
space: object.space_id || object.space,
|
space: object.space_id || object.space,
|
||||||
user: object.user_id || object.user,
|
user: object.user_id || object.user,
|
||||||
editor_name: object.editor_name,
|
editor_name: object.editor_name,
|
||||||
object: object
|
object: object.toJSON()
|
||||||
};
|
};
|
||||||
|
|
||||||
/*let action = new Action(attr);
|
let action = new Action(attr);
|
||||||
action.save(function(err) {
|
action.save(function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
console.error("saved create action err:", err);
|
console.error("saved create action err:", err);
|
||||||
});*/
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
module.exports = (req, res, next) => {
|
||||||
@ -34,21 +32,21 @@ module.exports = (req, res, next) => {
|
|||||||
|
|
||||||
res['distributeCreate'] = function(model, object) {
|
res['distributeCreate'] = function(model, object) {
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
redis.sendMessage("create", model, object, req.channelId);
|
redis.sendMessage("create", model, object.toJSON(), req.channelId);
|
||||||
this.status(201).json(object);
|
this.status(201).json(object.toJSON());
|
||||||
saveAction("create", object);
|
saveAction("create", object);
|
||||||
};
|
};
|
||||||
|
|
||||||
res['distributeUpdate'] = function(model, object) {
|
res['distributeUpdate'] = function(model, object) {
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
redis.sendMessage("update", model, object, req.channelId);
|
redis.sendMessage("update", model, object.toJSON(), req.channelId);
|
||||||
this.status(200).json(object);
|
this.status(200).json(object.toJSON());
|
||||||
saveAction("update", object);
|
saveAction("update", object);
|
||||||
};
|
};
|
||||||
|
|
||||||
res['distributeDelete'] = function(model, object) {
|
res['distributeDelete'] = function(model, object) {
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
redis.sendMessage("delete", model, object, req.channelId);
|
redis.sendMessage("delete", model, object.toJSON(), req.channelId);
|
||||||
this.sendStatus(204);
|
this.sendStatus(204);
|
||||||
saveAction("delete", object);
|
saveAction("delete", object);
|
||||||
};
|
};
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const db = require('../models/db');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
|
|
||||||
|
require('../models/schema');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
module.exports = (req, res, next) => {
|
||||||
var artifactId = req.params.artifact_id;
|
var artifactId = req.params.artifact_id;
|
||||||
db.Artifact.findOne({where: {
|
Artifact.findOne({
|
||||||
"_id": artifactId
|
"_id": artifactId
|
||||||
}}).then(artifact => {
|
}, (err, artifact) => {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
if (artifact) {
|
if (artifact) {
|
||||||
req['artifact'] = artifact;
|
req['artifact'] = artifact;
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('../models/db');
|
require('../models/schema');
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
|
||||||
@ -33,13 +33,13 @@ module.exports = (req, res, next) => {
|
|||||||
|
|
||||||
respond(origin, req, res, next);
|
respond(origin, req, res, next);
|
||||||
} else {
|
} else {
|
||||||
//Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => {
|
Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => {
|
||||||
//if (team) {
|
if (team) {
|
||||||
respond(origin, req, res, next);
|
respond(origin, req, res, next);
|
||||||
//} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
//}
|
}
|
||||||
//});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('../models/db');
|
require('../models/schema');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
module.exports = (req, res, next) => {
|
||||||
@ -10,8 +10,8 @@ module.exports = (req, res, next) => {
|
|||||||
req.i18n.setLocaleFromCookie();
|
req.i18n.setLocaleFromCookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.user && req.user.prefs_language) {
|
if (req.user && req.user.preferences.language) {
|
||||||
req.i18n.setLocale(req.user.prefs_language);
|
req.i18n.setLocale(req.user.preferences.language);
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
@ -1,47 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const db = require('../models/db');
|
|
||||||
var config = require('config');
|
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
|
||||||
const token = req.cookies["sdsession"];
|
|
||||||
|
|
||||||
if (token && token != "null" && token != null) {
|
|
||||||
db.Session.findOne({where: {token: token}})
|
|
||||||
.then(session => {
|
|
||||||
if (!session) {
|
|
||||||
// session not found
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else db.User.findOne({where: {_id: session.user_id}})
|
|
||||||
.then(user => {
|
|
||||||
if (!user) {
|
|
||||||
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname;
|
|
||||||
res.clearCookie('sdsession', { domain: domain });
|
|
||||||
|
|
||||||
if (req.accepts("text/html")) {
|
|
||||||
res.send("Please clear your cookies and try again.");
|
|
||||||
} else if (req.accepts('application/json')) {
|
|
||||||
res.status(403).json({
|
|
||||||
"error": "token_not_found"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.send("Please clear your cookies and try again.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
req["token"] = token;
|
|
||||||
req["user"] = user;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.error(err => {
|
|
||||||
console.error("Session resolve error",err);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
38
middlewares/setuser.js
Normal file
38
middlewares/setuser.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../models/schema');
|
||||||
|
var config = require('config');
|
||||||
|
|
||||||
|
module.exports = (req, res, next) => {
|
||||||
|
const token = req.cookies["sdsession"];
|
||||||
|
if (token && token != "null" && token !== null) {
|
||||||
|
User.findOne({
|
||||||
|
"sessions.token": token
|
||||||
|
}).populate('team').exec((err, user) => {
|
||||||
|
if (!user) {
|
||||||
|
// FIXME
|
||||||
|
var domain = "localhost";
|
||||||
|
res.clearCookie('sdsession', {
|
||||||
|
domain: domain
|
||||||
|
});
|
||||||
|
|
||||||
|
if (req.accepts("text/html")) {
|
||||||
|
res.redirect("/");
|
||||||
|
} else if (req.accepts('application/json')) {
|
||||||
|
res.status(403).json({
|
||||||
|
"error": "token_not_found"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
req["token"] = token;
|
||||||
|
req["user"] = user;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const db = require('../models/db');
|
require('../models/schema');
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
|
||||||
module.exports = (req, res, next) => {
|
module.exports = (req, res, next) => {
|
||||||
@ -19,6 +19,50 @@ module.exports = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var rolePerUser = (originalSpace, user, cb) => {
|
||||||
|
originalSpace.path = [];
|
||||||
|
|
||||||
|
if (originalSpace._id.equals(req.user.home_folder_id) || (originalSpace.creator && originalSpace.creator._id.equals(req.user._id))) {
|
||||||
|
cb("admin");
|
||||||
|
} else {
|
||||||
|
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
|
||||||
|
Membership.find({
|
||||||
|
"space": space._id
|
||||||
|
}, function(err, parentMemberships) {
|
||||||
|
var currentMemberships = parentMemberships.concat(allMemberships);
|
||||||
|
|
||||||
|
if (space.parent_space_id) {
|
||||||
|
Space.findOne({
|
||||||
|
"_id": space.parent_space_id
|
||||||
|
}, function(err, parentSpace) {
|
||||||
|
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// reached the top
|
||||||
|
|
||||||
|
var role = prevRole;
|
||||||
|
space.memberships = currentMemberships;
|
||||||
|
|
||||||
|
if(role == "none"){
|
||||||
|
if(originalSpace.access_mode == "public") {
|
||||||
|
role = "viewer";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMemberships.forEach(function(m, i) {
|
||||||
|
if (m.user && m.user.equals(user._id)) {
|
||||||
|
role = m.role;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cb(role);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
findMembershipsForSpace(originalSpace, [], "none");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var finalizeAnonymousLogin = function(space, spaceAuth) {
|
var finalizeAnonymousLogin = function(space, spaceAuth) {
|
||||||
var role = "none";
|
var role = "none";
|
||||||
|
|
||||||
@ -33,7 +77,7 @@ module.exports = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
db.getUserRoleInSpace(space, req.user, function(newRole) {
|
rolePerUser(space, req.user, function(newRole) {
|
||||||
if (newRole == "admin" && (role == "editor" || role == "viewer")) {
|
if (newRole == "admin" && (role == "editor" || role == "viewer")) {
|
||||||
finalizeReq(space, newRole);
|
finalizeReq(space, newRole);
|
||||||
} else if (newRole == "editor" && (role == "viewer")) {
|
} else if (newRole == "editor" && (role == "viewer")) {
|
||||||
@ -53,12 +97,16 @@ module.exports = (req, res, next) => {
|
|||||||
'email': 1
|
'email': 1
|
||||||
};
|
};
|
||||||
|
|
||||||
db.Space.findOne({where: {
|
Space.findOne({
|
||||||
"_id": spaceId
|
"_id": spaceId
|
||||||
}}).then(function(space) {
|
}).populate("creator", userMapping).exec(function(err, space) {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
if (space) {
|
if (space) {
|
||||||
|
|
||||||
if (space.access_mode == "public") {
|
if (space.access_mode == "public") {
|
||||||
|
|
||||||
if (space.password) {
|
if (space.password) {
|
||||||
if (req.spacePassword) {
|
if (req.spacePassword) {
|
||||||
if (req.spacePassword === space.password) {
|
if (req.spacePassword === space.password) {
|
||||||
@ -78,8 +126,6 @@ module.exports = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// space is private
|
|
||||||
|
|
||||||
// special permission for screenshot/pdf export from backend
|
// special permission for screenshot/pdf export from backend
|
||||||
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) {
|
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) {
|
||||||
finalizeReq(space, "viewer");
|
finalizeReq(space, "viewer");
|
||||||
@ -87,7 +133,7 @@ module.exports = (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
db.getUserRoleInSpace(space, req.user, function(role) {
|
rolePerUser(space, req.user, function(role) {
|
||||||
if (role == "none") {
|
if (role == "none") {
|
||||||
finalizeAnonymousLogin(space, req["spaceAuth"]);
|
finalizeAnonymousLogin(space, req["spaceAuth"]);
|
||||||
} else {
|
} else {
|
||||||
@ -109,5 +155,6 @@ module.exports = (req, res, next) => {
|
|||||||
"error": "space_not_found"
|
"error": "space_not_found"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
33
middlewares/subdomain.js
Normal file
33
middlewares/subdomain.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../models/schema');
|
||||||
|
var config = require('config');
|
||||||
|
|
||||||
|
module.exports = (req, res, next) => {
|
||||||
|
let host = req.headers.host;
|
||||||
|
Team.getTeamForHost(host, (err, team, subdomain) => {
|
||||||
|
if (subdomain) {
|
||||||
|
if (!err && team) {
|
||||||
|
req.subdomainTeam = team;
|
||||||
|
req.subdomain = subdomain;
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
if (req.accepts('text/html')) {
|
||||||
|
res.status(404).render('not_found', {
|
||||||
|
title: 'Page Not Found.'
|
||||||
|
});
|
||||||
|
} else if (req.accepts('application/json')) {
|
||||||
|
res.status(404).json({
|
||||||
|
"error": "not_found"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).render('not_found', {
|
||||||
|
title: 'Page Not Found.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
23
middlewares/team_helpers.js
Normal file
23
middlewares/team_helpers.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../models/schema');
|
||||||
|
var config = require('config');
|
||||||
|
|
||||||
|
module.exports = (req, res, next) => {
|
||||||
|
if (req.user) {
|
||||||
|
var isAdmin = req.user.team.admins.indexOf(req.user._id) >= 0;
|
||||||
|
var correctMethod = req.method == "GET" || (req.method == "DELETE" || req.method == "PUT" || req.method == "POST");
|
||||||
|
|
||||||
|
if (correctMethod && isAdmin) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.status(403, {
|
||||||
|
"error": "not authorized"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(403, {
|
||||||
|
"error": "not logged in"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
31
middlewares/templates.js
Normal file
31
middlewares/templates.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../models/schema');
|
||||||
|
var config = require('config');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
module.exports = (req, res, next) => {
|
||||||
|
res.oldRender = res.render;
|
||||||
|
res.render = function(template, params) {
|
||||||
|
|
||||||
|
var team = req.subdomainTeam;
|
||||||
|
if (team) {
|
||||||
|
team = _.pick(team.toObject(), ['_id', 'name', 'subdomain', 'avatar_original_uri']);
|
||||||
|
} else {
|
||||||
|
team = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addParams = {
|
||||||
|
locale: req.i18n.locale,
|
||||||
|
config: config,
|
||||||
|
subdomain_team: team,
|
||||||
|
user: req.user,
|
||||||
|
csrf_token: "",
|
||||||
|
socket_auth: req.token
|
||||||
|
};
|
||||||
|
|
||||||
|
const all = _.extend(params, addParams);
|
||||||
|
res.oldRender(template, all);
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// FIXME port this last model
|
|
||||||
|
|
||||||
var mongoose = require('mongoose');
|
var mongoose = require('mongoose');
|
||||||
var Schema = mongoose.Schema;
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
88
models/artifact.js
Normal file
88
models/artifact.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.artifactSchema = mongoose.model('Artifact', {
|
||||||
|
mime: String,
|
||||||
|
thumbnail_uri: String,
|
||||||
|
space_id: Schema.Types.ObjectId,
|
||||||
|
user_id: {type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
|
last_update_user_id: {type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
|
editor_name: String,
|
||||||
|
last_update_editor_name: String,
|
||||||
|
description: String,
|
||||||
|
state: {type: String, default: "idle"},
|
||||||
|
meta: {
|
||||||
|
linked_to: [String],
|
||||||
|
title: String,
|
||||||
|
tags: [String],
|
||||||
|
search_text: String,
|
||||||
|
link_uri: String,
|
||||||
|
play_from: Number,
|
||||||
|
play_to: Number,
|
||||||
|
},
|
||||||
|
board: {
|
||||||
|
x: {type: Number, default: 0.0},
|
||||||
|
y: {type: Number, default: 0.0},
|
||||||
|
z: {type: Number, default: 0.0},
|
||||||
|
r: {type: Number, default: 0.0},
|
||||||
|
w: {type: Number, default: 100},
|
||||||
|
h: {type: Number, default: 100},
|
||||||
|
},
|
||||||
|
control_points: [{
|
||||||
|
dx: Number, dy: Number
|
||||||
|
}],
|
||||||
|
group:{type: String, default: ""},
|
||||||
|
locked: {type: Boolean, default: false},
|
||||||
|
payload_uri: String,
|
||||||
|
payload_thumbnail_web_uri: String,
|
||||||
|
payload_thumbnail_medium_uri: String,
|
||||||
|
payload_thumbnail_big_uri: String,
|
||||||
|
payload_size: Number, // file size in bytes
|
||||||
|
style: {
|
||||||
|
fill_color: {type: String, default: "transparent"},
|
||||||
|
stroke_color:{type: String, default: "#000000"},
|
||||||
|
text_color: String,
|
||||||
|
stroke: {type: Number, default: 0.0},
|
||||||
|
stroke_style: {type: String, default: "solid"},
|
||||||
|
alpha: {type: Number, default: 1.0},
|
||||||
|
order: {type: Number, default: 0},
|
||||||
|
crop: {
|
||||||
|
x: Number,
|
||||||
|
y: Number,
|
||||||
|
w: Number,
|
||||||
|
h: Number
|
||||||
|
},
|
||||||
|
shape: String,
|
||||||
|
shape_svg: String,
|
||||||
|
padding_left: Number,
|
||||||
|
padding_right: Number,
|
||||||
|
padding_top: Number,
|
||||||
|
padding_bottom: Number,
|
||||||
|
margin_left: Number,
|
||||||
|
margin_right: Number,
|
||||||
|
margin_top: Number,
|
||||||
|
margin_bottom: Number,
|
||||||
|
border_radius: Number,
|
||||||
|
align: {type: String, default: "left"},
|
||||||
|
valign: {type: String, default: "top"},
|
||||||
|
brightness: Number,
|
||||||
|
contrast: Number,
|
||||||
|
saturation: Number,
|
||||||
|
blur: Number,
|
||||||
|
hue: Number,
|
||||||
|
opacity: Number
|
||||||
|
},
|
||||||
|
payload_alternatives: [{
|
||||||
|
mime: String,
|
||||||
|
payload_uri: String,
|
||||||
|
payload_thumbnail_web_uri: String,
|
||||||
|
payload_thumbnail_medium_uri: String,
|
||||||
|
payload_thumbnail_big_uri: String,
|
||||||
|
payload_size: Number
|
||||||
|
}],
|
||||||
|
created_at: {type: Date, default: Date.now},
|
||||||
|
created_from_ip: {type: String},
|
||||||
|
updated_at: {type: Date, default: Date.now}
|
||||||
|
});
|
364
models/db.js
364
models/db.js
@ -1,364 +0,0 @@
|
|||||||
const Umzug = require('umzug');
|
|
||||||
|
|
||||||
function sequel_log(a,b,c) {
|
|
||||||
console.log(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const sequelize = new Sequelize('database', 'username', 'password', {
|
|
||||||
host: 'localhost',
|
|
||||||
dialect: 'sqlite',
|
|
||||||
|
|
||||||
pool: {
|
|
||||||
max: 5,
|
|
||||||
min: 0,
|
|
||||||
acquire: 30000,
|
|
||||||
idle: 10000
|
|
||||||
},
|
|
||||||
|
|
||||||
// SQLite only
|
|
||||||
storage: 'database.sqlite',
|
|
||||||
logging: sequel_log,
|
|
||||||
|
|
||||||
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
|
|
||||||
operatorsAliases: false
|
|
||||||
});
|
|
||||||
|
|
||||||
var User;
|
|
||||||
var Session;
|
|
||||||
var Space;
|
|
||||||
var Membership;
|
|
||||||
var Artifact;
|
|
||||||
var Message;
|
|
||||||
var Action;
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
User: sequelize.define('user', {
|
|
||||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
email: Sequelize.STRING,
|
|
||||||
password_hash: Sequelize.STRING,
|
|
||||||
nickname: Sequelize.STRING,
|
|
||||||
avatar_original_uri: Sequelize.STRING,
|
|
||||||
avatar_thumb_uri: Sequelize.STRING,
|
|
||||||
confirmation_token: Sequelize.STRING,
|
|
||||||
password_reset_token: Sequelize.STRING,
|
|
||||||
home_folder_id: Sequelize.STRING,
|
|
||||||
prefs_language: Sequelize.STRING,
|
|
||||||
prefs_email_notifications: Sequelize.STRING,
|
|
||||||
prefs_email_digest: Sequelize.STRING,
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
|
|
||||||
}),
|
|
||||||
|
|
||||||
Session: sequelize.define('session', {
|
|
||||||
token: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
user_id: Sequelize.STRING,
|
|
||||||
expires: Sequelize.DATE,
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
device: Sequelize.STRING,
|
|
||||||
ip: Sequelize.STRING
|
|
||||||
}),
|
|
||||||
|
|
||||||
Space: sequelize.define('space', {
|
|
||||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
name: {type: Sequelize.STRING, default: "New Space"},
|
|
||||||
space_type: {type: Sequelize.STRING, defaultValue: "space"},
|
|
||||||
creator_id: Sequelize.STRING,
|
|
||||||
parent_space_id: Sequelize.STRING,
|
|
||||||
|
|
||||||
access_mode: {type: Sequelize.STRING, default: "private"}, // "public" || "private"
|
|
||||||
password: Sequelize.STRING,
|
|
||||||
edit_hash: Sequelize.STRING,
|
|
||||||
edit_slug: Sequelize.STRING,
|
|
||||||
editors_locking: Sequelize.BOOLEAN,
|
|
||||||
|
|
||||||
thumbnail_uri: Sequelize.STRING,
|
|
||||||
|
|
||||||
width: Sequelize.INTEGER,
|
|
||||||
height: Sequelize.INTEGER,
|
|
||||||
background_color: Sequelize.STRING,
|
|
||||||
background_uri: Sequelize.STRING,
|
|
||||||
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
thumbnail_url: Sequelize.STRING,
|
|
||||||
thumbnail_updated_at: {type: Sequelize.DATE}
|
|
||||||
}),
|
|
||||||
|
|
||||||
Membership: sequelize.define('membership', {
|
|
||||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
space_id: Sequelize.STRING,
|
|
||||||
user_id: Sequelize.STRING,
|
|
||||||
role: Sequelize.STRING,
|
|
||||||
code: Sequelize.STRING,
|
|
||||||
state: {type: Sequelize.STRING, defaultValue: "pending"}, // valid: "pending", "active"
|
|
||||||
email_invited: Sequelize.STRING,
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
|
|
||||||
}),
|
|
||||||
|
|
||||||
Message: sequelize.define('message', {
|
|
||||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
space_id: Sequelize.STRING,
|
|
||||||
user_id: Sequelize.STRING,
|
|
||||||
editor_name: Sequelize.STRING,
|
|
||||||
message: Sequelize.TEXT,
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
|
|
||||||
}),
|
|
||||||
|
|
||||||
Artifact: sequelize.define('artifact', {
|
|
||||||
_id: {type: Sequelize.STRING, primaryKey: true},
|
|
||||||
space_id: Sequelize.STRING,
|
|
||||||
user_id: Sequelize.STRING,
|
|
||||||
|
|
||||||
mime: Sequelize.STRING,
|
|
||||||
thumbnail_uri: Sequelize.STRING,
|
|
||||||
last_update_user_id: Sequelize.STRING,
|
|
||||||
editor_name: Sequelize.STRING,
|
|
||||||
last_update_editor_name: Sequelize.STRING,
|
|
||||||
description: Sequelize.TEXT,
|
|
||||||
state: {type: Sequelize.STRING, default: "idle"},
|
|
||||||
|
|
||||||
//linked_to: Sequelize.STRING,
|
|
||||||
title: Sequelize.STRING,
|
|
||||||
tags: Sequelize.TEXT,
|
|
||||||
search_text: Sequelize.STRING,
|
|
||||||
link_uri: Sequelize.STRING,
|
|
||||||
play_from: Sequelize.DECIMAL,
|
|
||||||
play_to: Sequelize.DECIMAL,
|
|
||||||
|
|
||||||
x: {type: Sequelize.DECIMAL, default: 0.0},
|
|
||||||
y: {type: Sequelize.DECIMAL, default: 0.0},
|
|
||||||
z: {type: Sequelize.DECIMAL, default: 0.0},
|
|
||||||
r: {type: Sequelize.DECIMAL, default: 0.0},
|
|
||||||
w: {type: Sequelize.DECIMAL, default: 100},
|
|
||||||
h: {type: Sequelize.DECIMAL, default: 100},
|
|
||||||
|
|
||||||
//control_points: [{
|
|
||||||
// dx: Number, dy: Number
|
|
||||||
//}],
|
|
||||||
|
|
||||||
control_points: Sequelize.TEXT,
|
|
||||||
|
|
||||||
group: Sequelize.STRING,
|
|
||||||
locked: {type: Sequelize.BOOLEAN, default: false},
|
|
||||||
|
|
||||||
payload_uri: Sequelize.STRING,
|
|
||||||
payload_thumbnail_web_uri: Sequelize.STRING,
|
|
||||||
payload_thumbnail_medium_uri: Sequelize.STRING,
|
|
||||||
payload_thumbnail_big_uri: Sequelize.STRING,
|
|
||||||
payload_size: Sequelize.INTEGER, // file size in bytes
|
|
||||||
|
|
||||||
fill_color: {type: Sequelize.STRING, default: "transparent"},
|
|
||||||
stroke_color: {type: Sequelize.STRING, default: "#000000"},
|
|
||||||
text_color: Sequelize.STRING,
|
|
||||||
stroke: {type: Sequelize.DECIMAL, default: 0.0},
|
|
||||||
stroke_style: {type: Sequelize.STRING, default: "solid"},
|
|
||||||
alpha: {type: Sequelize.DECIMAL, default: 1.0},
|
|
||||||
order: {type: Sequelize.INTEGER, default: 0},
|
|
||||||
crop_x: Sequelize.INTEGER,
|
|
||||||
crop_y: Sequelize.INTEGER,
|
|
||||||
crop_w: Sequelize.INTEGER,
|
|
||||||
crop_h: Sequelize.INTEGER,
|
|
||||||
shape: Sequelize.STRING,
|
|
||||||
shape_svg: Sequelize.STRING,
|
|
||||||
padding_left: Sequelize.INTEGER,
|
|
||||||
padding_right: Sequelize.INTEGER,
|
|
||||||
padding_top: Sequelize.INTEGER,
|
|
||||||
padding_bottom: Sequelize.INTEGER,
|
|
||||||
margin_left: Sequelize.INTEGER,
|
|
||||||
margin_right: Sequelize.INTEGER,
|
|
||||||
margin_top: Sequelize.INTEGER,
|
|
||||||
margin_bottom: Sequelize.INTEGER,
|
|
||||||
border_radius: Sequelize.INTEGER,
|
|
||||||
align: {type: Sequelize.STRING, default: "left"},
|
|
||||||
valign: {type: Sequelize.STRING, default: "top"},
|
|
||||||
|
|
||||||
brightness: Sequelize.DECIMAL,
|
|
||||||
contrast: Sequelize.DECIMAL,
|
|
||||||
saturation: Sequelize.DECIMAL,
|
|
||||||
blur: Sequelize.DECIMAL,
|
|
||||||
hue: Sequelize.DECIMAL,
|
|
||||||
opacity: Sequelize.DECIMAL,
|
|
||||||
|
|
||||||
payload_alternatives: Sequelize.TEXT,
|
|
||||||
|
|
||||||
/*payload_alternatives: [{
|
|
||||||
mime: String,
|
|
||||||
payload_uri: String,
|
|
||||||
payload_thumbnail_web_uri: String,
|
|
||||||
payload_thumbnail_medium_uri: String,
|
|
||||||
payload_thumbnail_big_uri: String,
|
|
||||||
payload_size: Number
|
|
||||||
}],*/
|
|
||||||
|
|
||||||
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
|
|
||||||
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
|
|
||||||
}),
|
|
||||||
|
|
||||||
init: async function() {
|
|
||||||
User = this.User;
|
|
||||||
Session = this.Session;
|
|
||||||
Space = this.Space;
|
|
||||||
Artifact = this.Artifact;
|
|
||||||
Message = this.Message;
|
|
||||||
Membership = this.Membership;
|
|
||||||
|
|
||||||
Space.belongsTo(User, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'creator_id'
|
|
||||||
},
|
|
||||||
as: 'creator'
|
|
||||||
});
|
|
||||||
|
|
||||||
Membership.belongsTo(User, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'user_id'
|
|
||||||
},
|
|
||||||
as: 'user'
|
|
||||||
});
|
|
||||||
|
|
||||||
Membership.belongsTo(Space, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'space_id'
|
|
||||||
},
|
|
||||||
as: 'space'
|
|
||||||
});
|
|
||||||
|
|
||||||
Artifact.belongsTo(User, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'user_id'
|
|
||||||
},
|
|
||||||
as: 'user'
|
|
||||||
});
|
|
||||||
|
|
||||||
Artifact.belongsTo(Space, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'space_id'
|
|
||||||
},
|
|
||||||
as: 'space'
|
|
||||||
});
|
|
||||||
|
|
||||||
Message.belongsTo(User, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'user_id'
|
|
||||||
},
|
|
||||||
as: 'user'
|
|
||||||
});
|
|
||||||
|
|
||||||
Message.belongsTo(Space, {
|
|
||||||
foreignKey: {
|
|
||||||
name: 'space_id'
|
|
||||||
},
|
|
||||||
as: 'space'
|
|
||||||
});
|
|
||||||
|
|
||||||
await sequelize.sync();
|
|
||||||
|
|
||||||
var umzug = new Umzug({
|
|
||||||
storage: 'sequelize',
|
|
||||||
storageOptions: {
|
|
||||||
sequelize: sequelize
|
|
||||||
},
|
|
||||||
migrations: {
|
|
||||||
params: [
|
|
||||||
sequelize.getQueryInterface(),
|
|
||||||
Sequelize
|
|
||||||
],
|
|
||||||
path: './models/migrations',
|
|
||||||
pattern: /\.js$/
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
umzug.up().then(function(migrations) {
|
|
||||||
console.log('Migration complete up!');
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
getUserRoleInSpace: (originalSpace, user, cb) => {
|
|
||||||
originalSpace.path = [];
|
|
||||||
|
|
||||||
if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) {
|
|
||||||
cb("admin");
|
|
||||||
} else {
|
|
||||||
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
|
|
||||||
Membership.findAll({ where: {
|
|
||||||
"space_id": space._id
|
|
||||||
}}).then(function(parentMemberships) {
|
|
||||||
var currentMemberships = parentMemberships.concat(allMemberships);
|
|
||||||
|
|
||||||
if (space.parent_space_id) {
|
|
||||||
Space.findOne({ where: {
|
|
||||||
"_id": space.parent_space_id
|
|
||||||
}}).then(function(parentSpace) {
|
|
||||||
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// reached the top
|
|
||||||
var role = prevRole;
|
|
||||||
space.memberships = currentMemberships;
|
|
||||||
|
|
||||||
if (role == "none") {
|
|
||||||
if (originalSpace.access_mode == "public") {
|
|
||||||
role = "viewer";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMemberships.forEach(function(m, i) {
|
|
||||||
if (m.user_id && m.user_id == user._id) {
|
|
||||||
role = m.role;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cb(role);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
findMembershipsForSpace(originalSpace, [], "none");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
spaceToObject: (space) => {
|
|
||||||
// FIXME TODO
|
|
||||||
return space;
|
|
||||||
},
|
|
||||||
|
|
||||||
findUserBySessionToken: (token, cb) => {
|
|
||||||
Session.findOne({where: {token: token}})
|
|
||||||
.then(session => {
|
|
||||||
if (!session) cb(null, null)
|
|
||||||
else User.findOne({where: {_id: session.user_id}})
|
|
||||||
.then(user => {
|
|
||||||
cb(null, user)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
unpackArtifact: (a) => {
|
|
||||||
if (a.tags && (typeof a.tags)=="string") {
|
|
||||||
a.tags = JSON.parse(a.tags);
|
|
||||||
}
|
|
||||||
if (a.control_points && (typeof a.control_points)=="string") {
|
|
||||||
a.control_points = JSON.parse(a.control_points);
|
|
||||||
}
|
|
||||||
if (a.payload_alternatives && (typeof a.payload_alternatives)=="string") {
|
|
||||||
a.payload_alternatives = JSON.parse(a.payload_alternatives);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
},
|
|
||||||
|
|
||||||
packArtifact: (a) => {
|
|
||||||
if (a.tags && (typeof a.tags)!="string") {
|
|
||||||
a.tags = JSON.stringify(a.tags);
|
|
||||||
}
|
|
||||||
if (a.control_points && (typeof a.control_points)!="string") {
|
|
||||||
a.control_points = JSON.stringify(a.control_points);
|
|
||||||
}
|
|
||||||
if (a.payload_alternatives && (typeof a.payload_alternatives)!="string") {
|
|
||||||
a.payload_alternatives = JSON.stringify(a.payload_alternatives);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
21
models/domain.js
Normal file
21
models/domain.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.domainSchema = mongoose.Schema({
|
||||||
|
domain: String,
|
||||||
|
edu: Boolean,
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.domainSchema.index({
|
||||||
|
domain: 1
|
||||||
|
});
|
45
models/membership.js
Normal file
45
models/membership.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.membershipSchema = mongoose.Schema({
|
||||||
|
user: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User'
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Space'
|
||||||
|
},
|
||||||
|
team: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Team'
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: String,
|
||||||
|
default: "viewer"
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
default: "active"
|
||||||
|
},
|
||||||
|
email_invited: String,
|
||||||
|
code: String,
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.membershipSchema.index({
|
||||||
|
user: 1,
|
||||||
|
space: 1,
|
||||||
|
team: 1,
|
||||||
|
code: 1
|
||||||
|
});
|
||||||
|
|
31
models/message.js
Normal file
31
models/message.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.messageSchema = mongoose.Schema({
|
||||||
|
user: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User'
|
||||||
|
},
|
||||||
|
editor_name: String,
|
||||||
|
space: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Space'
|
||||||
|
},
|
||||||
|
message: String,
|
||||||
|
created_from_ip: {type: String},
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.messageSchema.index({
|
||||||
|
space: 1,
|
||||||
|
user: 1
|
||||||
|
});
|
@ -1,79 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
up: function(migration, DataTypes) {
|
|
||||||
return Promise.all([
|
|
||||||
migration.changeColumn('memberships', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'CASCADE'
|
|
||||||
}
|
|
||||||
),
|
|
||||||
migration.changeColumn('artifacts', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'CASCADE'
|
|
||||||
}
|
|
||||||
),
|
|
||||||
migration.changeColumn('messages', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'CASCADE'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
down: function(migration, DataTypes) {
|
|
||||||
return Promise.all([
|
|
||||||
migration.changeColumn('memberships', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'NO ACTION'
|
|
||||||
}
|
|
||||||
),
|
|
||||||
migration.changeColumn('artifacts', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'NO ACTION'
|
|
||||||
}
|
|
||||||
),
|
|
||||||
migration.changeColumn('messages', 'space_id',
|
|
||||||
{
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
references: {
|
|
||||||
model: 'spaces',
|
|
||||||
key: '_id'
|
|
||||||
},
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
onUpdate: 'NO ACTION'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
44
models/plan.js
Normal file
44
models/plan.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
Plan = mongoose.model('Plan', {
|
||||||
|
key: String,
|
||||||
|
description: String,
|
||||||
|
limit_folders: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
limit_spaces: {
|
||||||
|
type: Number,
|
||||||
|
default: 500
|
||||||
|
},
|
||||||
|
limit_storage_bytes: {
|
||||||
|
type: Number,
|
||||||
|
default: 10737418240
|
||||||
|
},
|
||||||
|
plan_type: {
|
||||||
|
type: String,
|
||||||
|
default: "org"
|
||||||
|
},
|
||||||
|
price: Number,
|
||||||
|
public: Boolean,
|
||||||
|
recurring: {
|
||||||
|
type: String,
|
||||||
|
default: "month"
|
||||||
|
},
|
||||||
|
title: String,
|
||||||
|
trial_days: Number,
|
||||||
|
voucher_code: String,
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.planModel = Plan;
|
12
models/schema.js
Normal file
12
models/schema.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
|
||||||
|
User = mongoose.model('User', require('./user').userSchema);
|
||||||
|
Action = mongoose.model('Action', require('./action').actionSchema);
|
||||||
|
Space = mongoose.model('Space', require('./space').spaceSchema);
|
||||||
|
Artifact = mongoose.model('Artifact', require('./artifact').artifactSchema);
|
||||||
|
Team = mongoose.model('Team', require('./team').teamSchema);
|
||||||
|
Message = mongoose.model('Message', require('./message').messageSchema);
|
||||||
|
Membership = mongoose.model('Membership', require('./membership').membershipSchema);
|
||||||
|
Domain = mongoose.model('Domain', require('./domain').domainSchema);
|
273
models/space.js
Normal file
273
models/space.js
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
var async = require('async');
|
||||||
|
var _ = require("underscore");
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
module.exports.spaceSchema = Schema({
|
||||||
|
name: {type: String, default: "New Space"},
|
||||||
|
space_type: {type: String, default: "space"},
|
||||||
|
|
||||||
|
creator : { type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
|
parent_space_id: Schema.Types.ObjectId,
|
||||||
|
|
||||||
|
access_mode: {type: String, default: "private"}, // "public" || "private"
|
||||||
|
password: String,
|
||||||
|
edit_hash: String,
|
||||||
|
edit_slug: String,
|
||||||
|
editors_locking: Boolean,
|
||||||
|
|
||||||
|
thumbnail_uri: String,
|
||||||
|
stats: {
|
||||||
|
num_children: Number,
|
||||||
|
total_spaces: Number,
|
||||||
|
total_folders: Number,
|
||||||
|
storage_bytes: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
advanced: {
|
||||||
|
type: {
|
||||||
|
width: Number,
|
||||||
|
height: Number,
|
||||||
|
margin: Number,
|
||||||
|
background_color: String,
|
||||||
|
background_uri: String,
|
||||||
|
background_repeat: Boolean,
|
||||||
|
grid_size: Number,
|
||||||
|
grid_divisions: Number,
|
||||||
|
gutter: Number,
|
||||||
|
columns: Number,
|
||||||
|
column_max_width: Number,
|
||||||
|
columns_responsive: Number,
|
||||||
|
row_max_height: Number,
|
||||||
|
padding_horz: Number,
|
||||||
|
padding_vert: Number
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
width: 200,
|
||||||
|
height: 400,
|
||||||
|
margin: 0,
|
||||||
|
background_color: "rgba(255,255,255,1)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
blocked_at: {type: Date, default: Date.now},
|
||||||
|
created_at: {type: Date, default: Date.now},
|
||||||
|
updated_at: {type: Date, default: Date.now},
|
||||||
|
thumbnail_updated_at: {type: Date},
|
||||||
|
thumbnail_url: String
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.spaceSchema.index({ creator: 1, parent_space_id: 1, created_at: 1, updated_at: 1, edit_hash: 1});
|
||||||
|
module.exports.spaceSchema.statics.allForUser = function (user, callback) {
|
||||||
|
return this.find({user_id: user_id}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.spaceSchema.statics.getMemberships = function (err, callback) {
|
||||||
|
callback(null, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
var getRecursiveSubspacesForSpace = (parentSpace, cb) => {
|
||||||
|
if (parentSpace.space_type == "folder") {
|
||||||
|
Space.find({
|
||||||
|
"parent_space_id": parentSpace._id
|
||||||
|
}).exec((err, subspaces) => {
|
||||||
|
async.map(subspaces, (space, innerCb) => {
|
||||||
|
getRecursiveSubspacesForSpace(space, (err, spaces) => {
|
||||||
|
innerCb(err, spaces);
|
||||||
|
});
|
||||||
|
}, (err, subspaces) => {
|
||||||
|
var flattenSubspaces = _.flatten(subspaces);
|
||||||
|
flattenSubspaces.push(parentSpace);
|
||||||
|
cb(null, flattenSubspaces);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, [parentSpace]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.spaceSchema.statics.getRecursiveSubspacesForSpace = getRecursiveSubspacesForSpace;
|
||||||
|
|
||||||
|
var roleMapping = {
|
||||||
|
"none": 0,
|
||||||
|
"viewer": 1,
|
||||||
|
"editor": 2,
|
||||||
|
"admin": 3
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.spaceSchema.statics.roleInSpace = (originalSpace, user, cb) => {
|
||||||
|
if (user.home_folder_id.toString() === originalSpace._id.toString()) {
|
||||||
|
cb(null, "admin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalSpace.creator) {
|
||||||
|
if (originalSpace.creator._id.toString() === user._id.toString()) {
|
||||||
|
cb(null, "admin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
|
||||||
|
Membership.find({
|
||||||
|
"space": space._id
|
||||||
|
}, (err, parentMemberships) => {
|
||||||
|
var currentMemberships = parentMemberships.concat(allMemberships);
|
||||||
|
|
||||||
|
if (space.parent_space_id) {
|
||||||
|
Space.findOne({
|
||||||
|
"_id": space.parent_space_id
|
||||||
|
}, function(err, parentSpace) {
|
||||||
|
|
||||||
|
var role = prevRole;
|
||||||
|
if(role == "none"){
|
||||||
|
if(originalSpace.access_mode == "public") {
|
||||||
|
role = "viewer";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findMembershipsForSpace(parentSpace, currentMemberships, role);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// reached the top
|
||||||
|
var role = prevRole;
|
||||||
|
space.memberships = currentMemberships;
|
||||||
|
currentMemberships.forEach(function(m, i) {
|
||||||
|
if (m.user && m.user.equals(user._id)) {
|
||||||
|
if (m.role != null) {
|
||||||
|
if (roleMapping[m.role] > roleMapping[role]) {
|
||||||
|
role = m.role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cb(err, role);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
findMembershipsForSpace(originalSpace, [], "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.spaceSchema.statics.recursiveDelete = (space, cb) => {
|
||||||
|
space.remove(function(err) {
|
||||||
|
|
||||||
|
Action.remove({
|
||||||
|
space: space
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
console.error("removed actions for space: ", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
Membership.remove({
|
||||||
|
space: space
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
console.error("removed memberships for space: ", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (space.space_type === "folder") {
|
||||||
|
Space
|
||||||
|
.find({
|
||||||
|
parent_space_id: space._id
|
||||||
|
})
|
||||||
|
.exec(function(err, spaces) {
|
||||||
|
async.eachLimit(spaces, 10, function(subSpace, innerCb) {
|
||||||
|
module.exports.spaceSchema.statics.recursiveDelete(subSpace, function(err) {
|
||||||
|
innerCb(err);
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Artifact.find({
|
||||||
|
space_id: space._id
|
||||||
|
}, function(err, artifacts) {
|
||||||
|
if (err) cb(err);
|
||||||
|
else {
|
||||||
|
async.eachLimit(artifacts, 20, function(a, innerCb) {
|
||||||
|
a.remove(function(err) {
|
||||||
|
innerCb(null, a);
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var duplicateRecursiveSpace = (space, user, depth, cb, newParentSpace) => {
|
||||||
|
var newSpace = new Space(space);
|
||||||
|
newSpace._id = mongoose.Types.ObjectId();
|
||||||
|
|
||||||
|
if (newParentSpace) {
|
||||||
|
newSpace.parent_space_id = newParentSpace._id;
|
||||||
|
} else {
|
||||||
|
newSpace.name = newSpace.name + " (b)";
|
||||||
|
}
|
||||||
|
|
||||||
|
newSpace.creator = user;
|
||||||
|
newSpace.created_at = new Date();
|
||||||
|
newSpace.updated_at = new Date();
|
||||||
|
|
||||||
|
if (newSpace.space_type === "space") {
|
||||||
|
newSpace.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
newSpace.save(function(err) {
|
||||||
|
|
||||||
|
if (newSpace.space_type === "folder" && depth < 10) {
|
||||||
|
|
||||||
|
Space
|
||||||
|
.find({
|
||||||
|
parent_space_id: space._id
|
||||||
|
})
|
||||||
|
.exec(function(err, spaces) {
|
||||||
|
async.eachLimit(spaces, 10, function(subSpace, innerCb) {
|
||||||
|
|
||||||
|
duplicateRecursiveSpace(subSpace, user, ++depth, function(err, newSubSpace) {
|
||||||
|
innerCb(err, newSubSpace);
|
||||||
|
}, newSpace);
|
||||||
|
|
||||||
|
}, function(err, allNewSubspaces) {
|
||||||
|
cb(err, newSpace);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Artifact.find({
|
||||||
|
space_id: space._id
|
||||||
|
}, function(err, artifacts) {
|
||||||
|
if (err) innerCb(err);
|
||||||
|
else {
|
||||||
|
async.eachLimit(artifacts, 20, function(a, innerCb) {
|
||||||
|
var newArtifact = new Artifact(a);
|
||||||
|
newArtifact._id = mongoose.Types.ObjectId();
|
||||||
|
newArtifact.space_id = newSpace._id;
|
||||||
|
newArtifact.created_at = new Date();
|
||||||
|
newArtifact.updated_at = new Date();
|
||||||
|
|
||||||
|
newArtifact.save(function(err) {
|
||||||
|
innerCb(null, newArtifact);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, function(err, allNewArtifacts) {
|
||||||
|
cb(err, newSpace);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.spaceSchema.statics.duplicateSpace = duplicateRecursiveSpace;
|
70
models/team.js
Normal file
70
models/team.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.teamSchema = mongoose.Schema({
|
||||||
|
name: String,
|
||||||
|
subdomain: String,
|
||||||
|
creator: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User'
|
||||||
|
},
|
||||||
|
admins: [{
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User'
|
||||||
|
}],
|
||||||
|
invitation_codes: [String],
|
||||||
|
avatar_thumb_uri: String,
|
||||||
|
avatar_uri: String,
|
||||||
|
payment_type: {
|
||||||
|
type: String,
|
||||||
|
default: "auto"
|
||||||
|
},
|
||||||
|
payment_plan_key: String,
|
||||||
|
payment_subscription_id: String,
|
||||||
|
blocked_at: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
|
upgraded_at: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.teamSchema.index({
|
||||||
|
creator: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.teamSchema.statics.getTeamForHost = (host, cb) => {
|
||||||
|
|
||||||
|
if (host != "127.0.0.1:9000") { //phantomjs check
|
||||||
|
let subDomainParts = host.split('.');
|
||||||
|
|
||||||
|
if (subDomainParts.length > 2) {
|
||||||
|
const subdomain = subDomainParts[0];
|
||||||
|
|
||||||
|
if (subdomain != "www") {
|
||||||
|
Team.findOne({
|
||||||
|
subdomain: subdomain
|
||||||
|
}).exec((err, team) => {
|
||||||
|
cb(err, team, subdomain)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cb(null, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(null, null);
|
||||||
|
}
|
||||||
|
}
|
53
models/user.js
Normal file
53
models/user.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
module.exports.userSchema = mongoose.Schema({
|
||||||
|
email: String,
|
||||||
|
password_hash: String,
|
||||||
|
nickname: String,
|
||||||
|
account_type: {type: String, default: "email"},
|
||||||
|
created_at: {type: Date, default: Date.now},
|
||||||
|
updated_at: {type: Date, default: Date.now},
|
||||||
|
avatar_original_uri: String,
|
||||||
|
avatar_thumb_uri: String,
|
||||||
|
src: String,
|
||||||
|
confirmation_token: String,
|
||||||
|
confirmed_at: Date,
|
||||||
|
password_reset_token: String,
|
||||||
|
home_folder_id: Schema.Types.ObjectId,
|
||||||
|
team : { type: Schema.Types.ObjectId, ref: 'Team' },
|
||||||
|
preferences: {
|
||||||
|
language: String,
|
||||||
|
email_notifications: {type: Boolean, default: true},
|
||||||
|
daily_digest_last_send: Date,
|
||||||
|
daily_digest: {type: Boolean, default: true}
|
||||||
|
},
|
||||||
|
sessions: [
|
||||||
|
{
|
||||||
|
token: String,
|
||||||
|
expires: Date,
|
||||||
|
device: String,
|
||||||
|
ip: String,
|
||||||
|
created_at: Date
|
||||||
|
}
|
||||||
|
],
|
||||||
|
payment_info: String,
|
||||||
|
payment_plan_key: {type: String, default: "free"},
|
||||||
|
payment_customer_id: String,
|
||||||
|
payment_subscription_id: String,
|
||||||
|
payment_notification_state: Number
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.userSchema.index({
|
||||||
|
email: 1,
|
||||||
|
"sessions.token": 1,
|
||||||
|
team: 1,
|
||||||
|
created_at: 1,
|
||||||
|
home_folder_id: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.userSchema.statics.findBySessionToken = function (token, cb) {
|
||||||
|
return this.findOne({ "sessions.token": token}, cb);
|
||||||
|
};
|
96
package.json
96
package.json
@ -3,56 +3,84 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node spacedeck.js"
|
"start": "nodemon -e .js,.html bin/www",
|
||||||
},
|
"test": "mocha"
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "1.3.0",
|
"archiver": "1.3.0",
|
||||||
"async": "2.3.0",
|
"async": "2.3.0",
|
||||||
|
"aws-sdk": "2.39.0",
|
||||||
"basic-auth": "1.1.0",
|
"basic-auth": "1.1.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcrypt": "*",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "~1.17.1",
|
||||||
"cheerio": "0.22.0",
|
"cheerio": "0.22.0",
|
||||||
"config": "1.25.1",
|
"config": "1.25.1",
|
||||||
"cookie-parser": "~1.4.3",
|
"cookie-parser": "~1.4.3",
|
||||||
|
"csurf": "1.9.0",
|
||||||
|
"debug": "~2.6.3",
|
||||||
"execSync": "latest",
|
"execSync": "latest",
|
||||||
"express": "^4.16.4",
|
"express": "~4.15.2",
|
||||||
"file-type": "^7.6.0",
|
|
||||||
"glob": "7.1.1",
|
"glob": "7.1.1",
|
||||||
"gm": "^1.23.1",
|
"gm": "1.23.0",
|
||||||
"gulp": "^4.0.2",
|
"googleapis": "18.0.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp": "^3.9.0",
|
||||||
"gulp-sass": "^4.0.2",
|
"gulp-concat": "2.6.1",
|
||||||
|
"gulp-eslint": "*",
|
||||||
|
"gulp-express": "0.3.5",
|
||||||
|
"gulp-nodemon": "*",
|
||||||
|
"gulp-sass": "^3.1.0",
|
||||||
|
"gulp-uglify": "^2.1.2",
|
||||||
|
"gulp-util": "^3.0.6",
|
||||||
"helmet": "^3.5.0",
|
"helmet": "^3.5.0",
|
||||||
"i18n-2": "0.6.3",
|
"i18n-2": "0.6.3",
|
||||||
|
"ioredis": "2.5.0",
|
||||||
|
"lodash": "^4.3.0",
|
||||||
"log-timestamp": "latest",
|
"log-timestamp": "latest",
|
||||||
"mock-aws-s3": "^2.6.0",
|
"md5": "2.2.1",
|
||||||
"moment": "^2.19.3",
|
"moment": "2.18.1",
|
||||||
"morgan": "^1.9.1",
|
"mongoose": "4.9.3",
|
||||||
"node-phantom-simple": "2.2.4",
|
"morgan": "1.8.1",
|
||||||
"node-server-screenshot": "^0.2.1",
|
"node-sass-middleware": "0.11.0",
|
||||||
"nodemailer": "^4.6.7",
|
"pdfkit": "0.8.0",
|
||||||
"phantomjs-prebuilt": "^2.1.16",
|
|
||||||
"read-chunk": "^2.1.0",
|
|
||||||
"request": "^2.88.0",
|
|
||||||
"sanitize-html": "^1.11.1",
|
|
||||||
"sequelize": "^4.37.6",
|
|
||||||
"serve-favicon": "~2.4.2",
|
|
||||||
"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",
|
|
||||||
"validator": "7.0.0",
|
"validator": "7.0.0",
|
||||||
"ws": "3.3.1"
|
"node-phantom-simple": "2.2.4",
|
||||||
|
"phantomjs-prebuilt": "2.1.14",
|
||||||
|
"pm2": "latest",
|
||||||
|
"qr-image": "3.2.0",
|
||||||
|
"raven": "1.2.0",
|
||||||
|
"request": "2.81.0",
|
||||||
|
"sanitize-html": "^1.11.1",
|
||||||
|
"serve-favicon": "~2.4.2",
|
||||||
|
"swig": "1.4.2",
|
||||||
|
"slug": "0.9.1",
|
||||||
|
"underscore": "1.8.3",
|
||||||
|
"weak": "1.0.1",
|
||||||
|
"ws": "2.2.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"express": "^4.13.3",
|
||||||
|
"gulp": "^3.9.0",
|
||||||
|
"gulp-clean": "^0.3.2",
|
||||||
|
"gulp-concat": "^2.6.0",
|
||||||
|
"gulp-eslint": "^3.0.1",
|
||||||
|
"gulp-express": "^0.3.0",
|
||||||
|
"gulp-fingerprint": "^0.3.2",
|
||||||
|
"gulp-nodemon": "^2.0.4",
|
||||||
|
"gulp-rev": "^7.1.2",
|
||||||
|
"gulp-rev-all": "^0.9.7",
|
||||||
|
"gulp-rev-replace": "^0.4.3",
|
||||||
|
"gulp-sass": "^3.1.0",
|
||||||
|
"gulp-uglify": "^2.1.2",
|
||||||
|
"mocha": "*",
|
||||||
|
"nodemon": "*",
|
||||||
|
"should": "^11.2.1",
|
||||||
|
"supertest": "^3.0.0",
|
||||||
|
"winston": "^2.3.1"
|
||||||
},
|
},
|
||||||
"main": "app.js",
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"directories": {},
|
"main": "Gulpfile.js",
|
||||||
|
"directories": {
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/spacedeck/spacedeck-open.git"
|
"url": "https://github.com/spacedeck/spacedeck-open.git"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.8 KiB |
@ -1,69 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="17.370329mm"
|
|
||||||
height="17.370247mm"
|
|
||||||
viewBox="0 0 17.370329 17.370247"
|
|
||||||
version="1.1"
|
|
||||||
id="svg3417"
|
|
||||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
|
||||||
sodipodi:docname="sd6-icon-white.svg"
|
|
||||||
inkscape:export-filename="/home/mntmn/code/spacedeck-open/public/images/favicon.png"
|
|
||||||
inkscape:export-xdpi="93.585312"
|
|
||||||
inkscape:export-ydpi="93.585312">
|
|
||||||
<defs
|
|
||||||
id="defs3411" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="5.6"
|
|
||||||
inkscape:cx="68.901329"
|
|
||||||
inkscape:cy="26.613846"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:window-width="2560"
|
|
||||||
inkscape:window-height="1376"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata3414">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-61.618407,-79.672019)">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
|
|
||||||
d="m 69.103371,79.69206 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.0 KiB |
@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="17.370329mm"
|
|
||||||
height="17.370247mm"
|
|
||||||
viewBox="0 0 17.370329 17.370247"
|
|
||||||
version="1.1"
|
|
||||||
id="svg3417"
|
|
||||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
|
||||||
sodipodi:docname="sd6-icon.svg">
|
|
||||||
<defs
|
|
||||||
id="defs3411" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="5.6"
|
|
||||||
inkscape:cx="68.901329"
|
|
||||||
inkscape:cy="26.613846"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:window-width="2560"
|
|
||||||
inkscape:window-height="1376"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata3414">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-61.618407,-79.672019)">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
|
|
||||||
d="m 69.103371,79.69206 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.9 KiB |
@ -1,129 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="81.731232mm"
|
|
||||||
height="17.370247mm"
|
|
||||||
viewBox="0 0 81.731232 17.370247"
|
|
||||||
version="1.1"
|
|
||||||
id="svg2651"
|
|
||||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
|
||||||
sodipodi:docname="sd6-logo-black.svg">
|
|
||||||
<defs
|
|
||||||
id="defs2645" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="2.8"
|
|
||||||
inkscape:cx="80.852573"
|
|
||||||
inkscape:cy="-16.110417"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:window-width="2560"
|
|
||||||
inkscape:window-height="1376"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata2648">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-29.059958,-86.19285)">
|
|
||||||
<g
|
|
||||||
id="g3248">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
|
|
||||||
d="m 36.544922,86.212891 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
|
|
||||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
|
||||||
<g
|
|
||||||
id="g2614"
|
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,-523.78744,61.714265)">
|
|
||||||
<g
|
|
||||||
id="flowRoot1610-0-6-8-1-1"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;line-height:1.25;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.37800002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1"
|
|
||||||
transform="matrix(2.6369365,0,0,2.6369365,2045.0224,86.079903)"
|
|
||||||
aria-label="Spacedeck">
|
|
||||||
<path
|
|
||||||
id="path3214"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 52.795627,11.002199 h 2.693247 C 55.466146,8.82601 53.73315,7.3543837 51.028539,7.3543837 c -2.659155,0 -4.573974,1.4488985 -4.556928,3.6137233 -0.0057,1.767088 1.232985,2.76143 3.244397,3.221669 l 1.215938,0.284097 c 1.27844,0.295462 1.852317,0.642061 1.863681,1.295486 -0.01136,0.710244 -0.676152,1.204575 -1.806861,1.204575 -1.244349,0 -2.06255,-0.57956 -2.125052,-1.698905 h -2.693246 c 0.03409,2.721656 1.926182,4.022824 4.852389,4.022824 2.897797,0 4.613748,-1.312532 4.625112,-3.522812 -0.01136,-1.857999 -1.267076,-2.99439 -3.562586,-3.500084 l -1.000024,-0.227278 c -1.056844,-0.227279 -1.727315,-0.57956 -1.704587,-1.272758 0.0057,-0.636379 0.55115,-1.0966177 1.642085,-1.0966177 1.096618,0 1.698905,0.4943297 1.77277,1.3238957 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3216"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 57.068458,22.422928 h 2.778476 v -4.687613 h 0.05682 c 0.352281,0.806838 1.136391,1.53981 2.454605,1.53981 1.931864,0 3.48872,-1.5114 3.48872,-4.483062 0,-3.07962 -1.647767,-4.483063 -3.471674,-4.483063 -1.380715,0 -2.136415,0.806838 -2.471651,1.607994 h -0.08523 v -1.494355 h -2.750066 z m 2.721656,-7.636547 c 0,-1.426171 0.590923,-2.306874 1.607993,-2.306874 1.028434,0 1.59663,0.903431 1.59663,2.306874 0,1.409125 -0.568196,2.323919 -1.59663,2.323919 -1.01707,0 -1.607993,-0.909112 -1.607993,-2.323919 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3218"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 69.778991,19.297853 c 1.215939,0 2.056868,-0.471602 2.534152,-1.363669 h 0.06818 v 1.215938 h 2.613699 v -5.931961 c 0,-1.846635 -1.642085,-2.909161 -3.86373,-2.909161 -2.346647,0 -3.676224,1.181847 -3.897821,2.772794 l 2.562562,0.09091 c 0.119321,-0.556832 0.579559,-0.897749 1.312532,-0.897749 0.681834,0 1.113663,0.329553 1.113663,0.914794 v 0.02841 c 0,0.534104 -0.57956,0.647743 -2.068232,0.778428 -1.767088,0.147731 -3.244396,0.801156 -3.244396,2.73302 0,1.727315 1.198892,2.568244 2.869387,2.568244 z m 0.857975,-1.818226 c -0.642061,0 -1.096617,-0.306825 -1.096617,-0.886384 0,-0.562514 0.443193,-0.903431 1.232984,-1.022752 0.517058,-0.07387 1.153437,-0.187505 1.465945,-0.352282 v 0.829566 c 0,0.852293 -0.715927,1.431852 -1.602312,1.431852 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3220"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 80.767893,19.314899 c 2.454605,0 3.977369,-1.426171 4.051234,-3.585314 h -2.596653 c -0.102275,0.926159 -0.659107,1.431853 -1.420489,1.431853 -0.977296,0 -1.613675,-0.823883 -1.613675,-2.375057 0,-1.53981 0.642061,-2.363693 1.613675,-2.363693 0.795474,0 1.312532,0.539785 1.420489,1.431852 h 2.596653 c -0.0625,-2.147779 -1.630721,-3.54554 -4.056916,-3.54554 -2.744384,0 -4.403515,1.82959 -4.403515,4.505791 0,2.664836 1.647767,4.500108 4.409197,4.500108 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3222"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 90.336304,19.314899 c 2.289828,0 3.795546,-1.107981 4.113736,-2.823932 l -2.551198,-0.07387 c -0.215914,0.579559 -0.78411,0.892067 -1.5114,0.892067 -1.068208,0 -1.727315,-0.710245 -1.727315,-1.778452 v -0.07386 h 5.818322 v -0.693199 c 0,-2.875069 -1.750042,-4.454653 -4.227374,-4.454653 -2.636427,0 -4.32965,1.806862 -4.32965,4.511473 0,2.795521 1.670495,4.494426 4.414879,4.494426 z m -1.676177,-5.471723 c 0.03977,-0.869339 0.727291,-1.528446 1.647767,-1.528446 0.914795,0 1.573902,0.636379 1.585266,1.528446 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3224"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 99.10072,19.275125 c 1.31821,0 2.10232,-0.732972 2.4546,-1.53981 h 0.0852 v 1.414807 h 2.75007 V 7.5134784 h -2.77848 v 4.4035156 h -0.0568 C 101.22577,11.115838 100.46439,10.309 99.089356,10.309 c -1.823907,0 -3.477356,1.403443 -3.477356,4.483063 0,2.971662 1.562537,4.483062 3.48872,4.483062 z m 0.96593,-2.164825 c -1.028432,0 -1.602309,-0.914794 -1.602309,-2.323919 0,-1.403443 0.568196,-2.306874 1.602309,-2.306874 1.01707,0 1.608,0.880703 1.608,2.306874 0,1.414807 -0.59661,2.323919 -1.608,2.323919 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3226"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 110.24303,19.314899 c 2.28983,0 3.79555,-1.107981 4.11374,-2.823932 l -2.5512,-0.07387 c -0.21591,0.579559 -0.78411,0.892067 -1.5114,0.892067 -1.06821,0 -1.72731,-0.710245 -1.72731,-1.778452 v -0.07386 h 5.81832 v -0.693199 c 0,-2.875069 -1.75004,-4.454653 -4.22737,-4.454653 -2.63643,0 -4.32965,1.806862 -4.32965,4.511473 0,2.795521 1.67049,4.494426 4.41487,4.494426 z m -1.67617,-5.471723 c 0.0398,-0.869339 0.72729,-1.528446 1.64777,-1.528446 0.91479,0 1.5739,0.636379 1.58526,1.528446 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3228"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 119.89384,19.314899 c 2.4546,0 3.97736,-1.426171 4.05123,-3.585314 h -2.59665 c -0.10228,0.926159 -0.65911,1.431853 -1.42049,1.431853 -0.9773,0 -1.61368,-0.823883 -1.61368,-2.375057 0,-1.53981 0.64206,-2.363693 1.61368,-2.363693 0.79547,0 1.31253,0.539785 1.42049,1.431852 h 2.59665 c -0.0625,-2.147779 -1.63072,-3.54554 -4.05692,-3.54554 -2.74438,0 -4.40351,1.82959 -4.40351,4.505791 0,2.664836 1.64777,4.500108 4.4092,4.500108 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
id="path3230"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
d="m 125.3826,19.150122 h 2.77848 v -2.619381 l 0.56251,-0.681835 2.0796,3.301216 h 3.2103 l -3.22167,-4.926255 3.09667,-3.801228 h -3.1478 l -2.45461,3.125076 h -0.125 V 7.5134784 h -2.77848 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
d="m 2146.72,133.51812 a 23.030019,11.514995 45 0 1 -24.427,-8.1423 23.030019,11.514995 45 0 1 -8.1423,-24.427 23.030019,11.514995 45 0 1 24.427,8.1423 23.030019,11.514995 45 0 1 8.1423,24.427 z m -16.2137,16.2138 a 23.030019,11.514995 45 0 1 -24.427,-8.1424 23.030019,11.514995 45 0 1 -8.1424,-24.4269 23.030019,11.514995 45 0 1 24.4271,8.1422 23.030019,11.514995 45 0 1 8.1423,24.4271 z"
|
|
||||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1"
|
|
||||||
id="path1681-6-5-3-7-4-9-2-0-2-7-6"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 158 KiB |
@ -133,6 +133,10 @@ function load_spaces(id, is_home, on_success, on_error) {
|
|||||||
}, on_error);
|
}, on_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function load_writable_folders( on_success, on_error) {
|
||||||
|
load_resource("get", "/spaces?writablefolders=true", null, on_success, on_error);
|
||||||
|
}
|
||||||
|
|
||||||
function load_history(s, on_success, on_error) {
|
function load_history(s, on_success, on_error) {
|
||||||
load_resource("get", "/spaces/"+ s._id +"/digest", null, on_success, on_error);
|
load_resource("get", "/spaces/"+ s._id +"/digest", null, on_success, on_error);
|
||||||
}
|
}
|
||||||
@ -178,10 +182,12 @@ function delete_space(s, on_success, on_error) {
|
|||||||
load_resource("delete", "/spaces/"+s._id, null, on_success, on_error);
|
load_resource("delete", "/spaces/"+s._id, null, on_success, on_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function delete_artifact(a, on_success, on_error) {
|
function delete_artifact(a, on_success, on_error) {
|
||||||
load_resource("delete", "/spaces/"+a.space_id+"/artifacts/"+a._id);
|
load_resource("delete", "/spaces/"+a.space_id+"/artifacts/"+a._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function duplicate_space(s, to_space_id, on_success, on_error) {
|
function duplicate_space(s, to_space_id, on_success, on_error) {
|
||||||
var path = "/spaces/"+s._id+"/duplicate";
|
var path = "/spaces/"+s._id+"/duplicate";
|
||||||
if(to_space_id) {
|
if(to_space_id) {
|
||||||
@ -260,8 +266,8 @@ function delete_user(u, password, on_success, on_error) {
|
|||||||
load_resource("delete", "/users/"+u._id +"?password="+password,null,on_success,on_error);
|
load_resource("delete", "/users/"+u._id +"?password="+password,null,on_success,on_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_user(name, email, password, password_confirmation, invite_code, on_success, on_error) {
|
function create_user(name, email, password, password_confirmation, on_success, on_error) {
|
||||||
load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation, invite_code: invite_code}, on_success, on_error);
|
load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation}, on_success, on_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_session(email, password, on_success, on_error) {
|
function create_session(email, password, on_success, on_error) {
|
||||||
|
15931
public/javascripts/spacedeck.js
Normal file
15931
public/javascripts/spacedeck.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,21 +8,23 @@ SpacedeckAccount = {
|
|||||||
account_confirmed_sent: false,
|
account_confirmed_sent: false,
|
||||||
account_tab: 'invoices',
|
account_tab: 'invoices',
|
||||||
password_change_error: null,
|
password_change_error: null,
|
||||||
feedback_text: "",
|
feedback_text: ""
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
show_account: function() {
|
show_account: function(user) {
|
||||||
this.activate_dropdown('account');
|
this.activate_dropdown('account');
|
||||||
|
this.load_subscription();
|
||||||
|
this.load_billing();
|
||||||
},
|
},
|
||||||
|
|
||||||
account_save_user_digest: function(val) {
|
account_save_user_digest: function(val) {
|
||||||
this.user.prefs_email_digest = val;
|
this.user.preferences.daily_digest = val;
|
||||||
this.save_user(function(){
|
this.save_user(function(){
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
account_save_user_notifications: function(val) {
|
account_save_user_notifications: function(val) {
|
||||||
this.user.prefs_email_notifications = val;
|
this.user.preferences.email_notifications = val;
|
||||||
this.save_user(function(){
|
this.save_user(function(){
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -34,11 +36,13 @@ SpacedeckAccount = {
|
|||||||
|
|
||||||
save_user_language: function(lang) {
|
save_user_language: function(lang) {
|
||||||
localStorage.lang = lang;
|
localStorage.lang = lang;
|
||||||
this.user.prefs_language = lang;
|
if (this.user.preferences) {
|
||||||
|
this.user.preferences.language = lang;
|
||||||
this.save_user(function() {
|
this.save_user(function() {
|
||||||
window._spacedeck_location_change = true;
|
window._spacedeck_location_change = true;
|
||||||
location.href="/spaces";
|
location.href="/spaces";
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
save_user: function(on_success) {
|
save_user: function(on_success) {
|
||||||
|
@ -41,7 +41,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
if ("medium_for_object" in this) {
|
if ("medium_for_object" in this) {
|
||||||
var medium = this.medium_for_object[a._id];
|
var medium = this.medium_for_object[a._id];
|
||||||
if (medium && a._id != this.editing_artifact_id) {
|
if (medium && a._id != this.editing_artifact_id) {
|
||||||
medium.value(a.description.toString());
|
medium.value(a.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -61,16 +61,16 @@ var SpacedeckBoardArtifacts = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
artifact_link: function(a) {
|
artifact_link: function(a) {
|
||||||
if (a.link_uri) {
|
if (a.meta && a.meta.link_uri) {
|
||||||
return a.link_uri;
|
return a.meta.link_uri;
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
artifact_link_caption: function(a) {
|
artifact_link_caption: function(a) {
|
||||||
if (a.link_uri) {
|
if (a.meta && a.meta.link_uri) {
|
||||||
var parts = a.link_uri.split("/");
|
var parts = a.meta.link_uri.split("/");
|
||||||
// scheme://domain.foo/...
|
// scheme://domain.foo/...
|
||||||
// 0 1 2
|
// 0 1 2
|
||||||
if (parts.length>2) {
|
if (parts.length>2) {
|
||||||
@ -89,8 +89,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
artifact_is_text_blank: function(a) {
|
artifact_is_text_blank: function(a) {
|
||||||
if(a.description){
|
if(a.description){
|
||||||
desc = a.description.toString();
|
var filtered = a.description.replace(/<[^>]+>/g,"").replace(/\s/g,"");
|
||||||
var filtered = desc.replace(/<[^>]+>/g,"").replace(/\s/g,"");
|
|
||||||
return (filtered.length<1);
|
return (filtered.length<1);
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
@ -103,8 +102,10 @@ var SpacedeckBoardArtifacts = {
|
|||||||
if (this.artifact_is_selected(a) && this.editing_artifact_id!=a._id) clzs.push("selected");
|
if (this.artifact_is_selected(a) && this.editing_artifact_id!=a._id) clzs.push("selected");
|
||||||
if (!a._id) clzs.push("creating");
|
if (!a._id) clzs.push("creating");
|
||||||
|
|
||||||
if (a.align) clzs.push("align-"+a.align);
|
if (a.style) {
|
||||||
if (a.valign) clzs.push("align-"+a.valign);
|
clzs.push("align-"+a.style.align);
|
||||||
|
clzs.push("align-"+a.style.valign);
|
||||||
|
}
|
||||||
|
|
||||||
clzs.push("state-"+a.state);
|
clzs.push("state-"+a.state);
|
||||||
|
|
||||||
@ -122,56 +123,56 @@ var SpacedeckBoardArtifacts = {
|
|||||||
artifact_inner_style: function(a) {
|
artifact_inner_style: function(a) {
|
||||||
var styles = [];
|
var styles = [];
|
||||||
|
|
||||||
//if (a.style) {
|
if (a.style) {
|
||||||
|
|
||||||
var svg_style = ((a.mime.match("vector") || a.mime.match("shape")) && a.shape!="square");
|
var svg_style = ((a.mime.match("vector") || a.mime.match("shape")) && a.style.shape!="square");
|
||||||
|
|
||||||
if (!svg_style) {
|
if (!svg_style) {
|
||||||
if (a.stroke) {
|
if (a.style.stroke) {
|
||||||
styles.push("border-width:"+a.stroke+"px");
|
styles.push("border-width:"+a.style.stroke+"px");
|
||||||
styles.push("border-style:"+(a.stroke_style||"solid"));
|
styles.push("border-style:"+(a.style.stroke_style||"solid"));
|
||||||
}
|
}
|
||||||
if (a.stroke_color) {
|
if (a.style.stroke_color) {
|
||||||
styles.push("border-color:"+a.stroke_color);
|
styles.push("border-color:"+a.style.stroke_color);
|
||||||
}
|
}
|
||||||
if (a.border_radius) {
|
if (a.style.border_radius) {
|
||||||
styles.push("border-radius:"+a.border_radius+"px");
|
styles.push("border-radius:"+a.style.border_radius+"px");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.fill_color && !svg_style) {
|
if (a.style.fill_color && !svg_style) {
|
||||||
styles.push("background-color:"+a.fill_color);
|
styles.push("background-color:"+a.style.fill_color);
|
||||||
}
|
}
|
||||||
if (a.text_color) {
|
if (a.style.text_color) {
|
||||||
styles.push("color:"+a.text_color);
|
styles.push("color:"+a.style.text_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
var filters = [];
|
var filters = [];
|
||||||
|
|
||||||
if (!isNaN(a.brightness) && a.brightness != 100) {
|
if (!isNaN(a.style.brightness) && a.style.brightness != 100) {
|
||||||
filters.push("brightness("+a.brightness+"%)");
|
filters.push("brightness("+a.style.brightness+"%)");
|
||||||
}
|
}
|
||||||
if (!isNaN(a.contrast) && a.contrast != 100) {
|
if (!isNaN(a.style.contrast) && a.style.contrast != 100) {
|
||||||
filters.push("contrast("+a.contrast+"%)");
|
filters.push("contrast("+a.style.contrast+"%)");
|
||||||
}
|
}
|
||||||
if (!isNaN(a.opacity) && a.opacity != 100) {
|
if (!isNaN(a.style.opacity) && a.style.opacity != 100) {
|
||||||
filters.push("opacity("+a.opacity+"%)");
|
filters.push("opacity("+a.style.opacity+"%)");
|
||||||
}
|
}
|
||||||
if (!isNaN(a.hue) && a.hue) {
|
if (!isNaN(a.style.hue) && a.style.hue) {
|
||||||
filters.push("hue-rotate("+a.hue+"deg)");
|
filters.push("hue-rotate("+a.style.hue+"deg)");
|
||||||
}
|
}
|
||||||
if (!isNaN(a.saturation) && a.saturation != 100) {
|
if (!isNaN(a.style.saturation) && a.style.saturation != 100) {
|
||||||
filters.push("saturate("+a.saturation+"%)");
|
filters.push("saturate("+a.style.saturation+"%)");
|
||||||
}
|
}
|
||||||
if (!isNaN(a.blur) && a.blur) {
|
if (!isNaN(a.style.blur) && a.style.blur) {
|
||||||
filters.push("blur("+a.blur+"px)");
|
filters.push("blur("+a.style.blur+"px)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters.length) {
|
if (filters.length) {
|
||||||
styles.push("-webkit-filter:"+filters.join(" "));
|
styles.push("-webkit-filter:"+filters.join(" "));
|
||||||
styles.push("filter:"+filters.join(" "));
|
styles.push("filter:"+filters.join(" "));
|
||||||
}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
return styles.join(";");
|
return styles.join(";");
|
||||||
},
|
},
|
||||||
@ -179,10 +180,12 @@ var SpacedeckBoardArtifacts = {
|
|||||||
artifact_text_cell_style: function(a, for_text_editor) {
|
artifact_text_cell_style: function(a, for_text_editor) {
|
||||||
var styles = [];
|
var styles = [];
|
||||||
|
|
||||||
if (a.padding_left) styles.push("padding-left:"+a.padding_left+"px");
|
if (a.style) {
|
||||||
if (a.padding_right) styles.push("padding-right:"+a.padding_right+"px");
|
if (a.style.padding_left) styles.push("padding-left:"+a.style.padding_left+"px");
|
||||||
if (a.padding_top) styles.push("padding-top:"+a.padding_top+"px");
|
if (a.style.padding_right) styles.push("padding-right:"+a.style.padding_right+"px");
|
||||||
if (a.padding_bottom) styles.push("padding-bottom:"+a.padding_bottom+"px");
|
if (a.style.padding_top) styles.push("padding-top:"+a.style.padding_top+"px");
|
||||||
|
if (a.style.padding_bottom) styles.push("padding-bottom:"+a.style.padding_bottom+"px");
|
||||||
|
}
|
||||||
|
|
||||||
return styles.join(";");
|
return styles.join(";");
|
||||||
},
|
},
|
||||||
@ -191,21 +194,25 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var styles = [];
|
var styles = [];
|
||||||
var z = 0;
|
var z = 0;
|
||||||
|
|
||||||
z = a.z;
|
if (a.board) {
|
||||||
|
z = a.board.z;
|
||||||
if (z<0) z=0; // fix negative z-index
|
if (z<0) z=0; // fix negative z-index
|
||||||
|
|
||||||
styles = [
|
styles = [
|
||||||
"left:" +a.x+"px",
|
"left:" +a.board.x+"px",
|
||||||
"top:" +a.y+"px",
|
"top:" +a.board.y+"px",
|
||||||
"width:" +a.w+"px",
|
"width:" +a.board.w+"px",
|
||||||
"height:"+a.h+"px",
|
"height:"+a.board.h+"px",
|
||||||
"z-index:"+z
|
"z-index:"+z
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
if (a.margin_left) styles.push("margin-left:"+a.margin_left+"px");
|
if (a.style) {
|
||||||
if (a.margin_right) styles.push("margin-right:"+a.margin_right+"px");
|
if (a.style.margin_left) styles.push("margin-left:"+a.style.margin_left+"px");
|
||||||
if (a.margin_top) styles.push("margin-top:"+a.margin_top+"px");
|
if (a.style.margin_right) styles.push("margin-right:"+a.style.margin_right+"px");
|
||||||
if (a.margin_bottom) styles.push("margin-bottom:"+a.margin_bottom+"px");
|
if (a.style.margin_top) styles.push("margin-top:"+a.style.margin_top+"px");
|
||||||
|
if (a.style.margin_bottom) styles.push("margin-bottom:"+a.style.margin_bottom+"px");
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: via class logic?
|
// FIXME: via class logic?
|
||||||
if (a.mime.match("vector")) {
|
if (a.mime.match("vector")) {
|
||||||
@ -234,7 +241,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
artifact_thumbnail_uri: function(a) {
|
artifact_thumbnail_uri: function(a) {
|
||||||
if (a.payload_thumbnail_big_uri && a.board) {
|
if (a.payload_thumbnail_big_uri && a.board) {
|
||||||
if (a.w>800) {
|
if (a.board.w>800) {
|
||||||
return a.payload_thumbnail_big_uri;
|
return a.payload_thumbnail_big_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,35 +255,35 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var type = parts[0];
|
var type = parts[0];
|
||||||
var provider = parts[1];
|
var provider = parts[1];
|
||||||
|
|
||||||
if (!a.link_uri) {
|
if (!a.meta || !a.meta.link_uri) {
|
||||||
console.log("missing meta / link_uri: ",a);
|
console.log("missing meta / link_uri: ",a);
|
||||||
console.log("type/provider: ",type,provider);
|
console.log("type/provider: ",type,provider);
|
||||||
return ("missing metadata: "+a._id);
|
return ("missing metadata: "+a._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider=="youtube") {
|
if (provider=="youtube") {
|
||||||
var vid = a.link_uri.match(/(v=|\/)([a-zA-Z0-9\-_]{11})/);
|
var vid = a.meta.link_uri.match(/(v=|\/)([a-zA-Z0-9\-_]{11})/);
|
||||||
if (vid && vid.length>2) {
|
if (vid && vid.length>2) {
|
||||||
var uri = "https://youtube.com/embed/"+vid[2];
|
var uri = "https://youtube.com/embed/"+vid[2];
|
||||||
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"?showinfo=0&rel=0&controls=0\"></iframe>";
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"?showinfo=0&rel=0&controls=0\"></iframe>";
|
||||||
} else return "Can't resolve: "+a.payload_uri;
|
} else return "Can't resolve: "+a.payload_uri;
|
||||||
|
|
||||||
} else if (provider=="dailymotion") {
|
} else if (provider=="dailymotion") {
|
||||||
var match = a.link_uri.match(/dailymotion.com\/video\/([^<]*)/);
|
var match = a.meta.link_uri.match(/dailymotion.com\/video\/([^<]*)/);
|
||||||
if (match && match.length>1) {
|
if (match && match.length>1) {
|
||||||
var uri = "https://www.dailymotion.com/embed/video/"+match[1];
|
var uri = "https://www.dailymotion.com/embed/video/"+match[1];
|
||||||
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
||||||
} else return "Can't resolve: "+a.payload_uri;
|
} else return "Can't resolve: "+a.payload_uri;
|
||||||
|
|
||||||
} else if (provider=="vimeo") {
|
} else if (provider=="vimeo") {
|
||||||
var match = a.link_uri.match(/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
|
var match = a.meta.link_uri.match(/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
|
||||||
if (match) {
|
if (match) {
|
||||||
var uri = "https://player.vimeo.com/video/"+match[2];
|
var uri = "https://player.vimeo.com/video/"+match[2];
|
||||||
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
||||||
} else return "Can't resolve: "+a.payload_uri;
|
} else return "Can't resolve: "+a.payload_uri;
|
||||||
|
|
||||||
} else if (provider=="soundcloud") {
|
} else if (provider=="soundcloud") {
|
||||||
return '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url='+a.link_uri.replace(":", "%3A")+'"></iframe>';
|
return '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url='+a.meta.link_uri.replace(":", "%3A")+'"></iframe>';
|
||||||
|
|
||||||
} else if (provider=="spacedeck") {
|
} else if (provider=="spacedeck") {
|
||||||
|
|
||||||
@ -292,8 +299,8 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
if (mtype != "vector" && mtype != "shape") return "";
|
if (mtype != "vector" && mtype != "shape") return "";
|
||||||
|
|
||||||
var shape = a.shape || "";
|
var shape = a.style.shape || "";
|
||||||
var padding = 32 + a.stroke*2;
|
var padding = 32 + a.style.stroke*2;
|
||||||
var path_svg;
|
var path_svg;
|
||||||
var fill = "";
|
var fill = "";
|
||||||
|
|
||||||
@ -303,13 +310,13 @@ var SpacedeckBoardArtifacts = {
|
|||||||
fill = "fill:none";
|
fill = "fill:none";
|
||||||
} else {
|
} else {
|
||||||
path_svg = render_vector_shape(a, padding);
|
path_svg = render_vector_shape(a, padding);
|
||||||
fill = "fill:"+a.fill_color+";";
|
fill = "fill:"+a.style.fill_color+";";
|
||||||
padding = 0;
|
padding = 0;
|
||||||
}
|
}
|
||||||
var margin = padding;
|
var margin = padding;
|
||||||
|
|
||||||
var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='"+(a.w+2*padding)+"' height='"+(a.h+2*padding)+"' ";
|
var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='"+(a.board.w+2*padding)+"' height='"+(a.board.h+2*padding)+"' ";
|
||||||
svg += "style='margin-left:"+(-margin)+"px;margin-top:"+(-margin)+"px;stroke-width:"+a.stroke+";stroke:"+a.stroke_color+";"+fill+"'>";
|
svg += "style='margin-left:"+(-margin)+"px;margin-top:"+(-margin)+"px;stroke-width:"+a.style.stroke+";stroke:"+a.style.stroke_color+";"+fill+"'>";
|
||||||
svg += path_svg;
|
svg += path_svg;
|
||||||
svg += "</svg>";
|
svg += "</svg>";
|
||||||
|
|
||||||
@ -322,10 +329,10 @@ var SpacedeckBoardArtifacts = {
|
|||||||
if (arts.length==0) return null;
|
if (arts.length==0) return null;
|
||||||
|
|
||||||
r = {
|
r = {
|
||||||
x1: parseInt(_.min(arts.map(function(a){return a.x}))),
|
x1: parseInt(_.min(arts.map(function(a){return a.board.x}))),
|
||||||
y1: parseInt(_.min(arts.map(function(a){return a.y}))),
|
y1: parseInt(_.min(arts.map(function(a){return a.board.y}))),
|
||||||
x2: parseInt(_.max(arts.map(function(a){return a.x+a.w}))),
|
x2: parseInt(_.max(arts.map(function(a){return a.board.x+a.board.w}))),
|
||||||
y2: parseInt(_.max(arts.map(function(a){return a.y+a.h})))
|
y2: parseInt(_.max(arts.map(function(a){return a.board.y+a.board.h})))
|
||||||
};
|
};
|
||||||
r.x=r.x1;
|
r.x=r.x1;
|
||||||
r.y=r.y1;
|
r.y=r.y1;
|
||||||
@ -349,7 +356,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
artifacts_in_rect: function(rect) {
|
artifacts_in_rect: function(rect) {
|
||||||
return _.filter(this.active_space_artifacts, function(a) {
|
return _.filter(this.active_space_artifacts, function(a) {
|
||||||
return this.rects_intersecting(a, rect);
|
return this.rects_intersecting(a.board, rect);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -359,15 +366,15 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a)}.bind(this));
|
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; });
|
var max_z = _.max(overlapping,function(a){ return a.board.z; });
|
||||||
if (max_z.board) {
|
if (max_z.board) {
|
||||||
max_z = max_z.z + 1;
|
max_z = max_z.board.z + 1;
|
||||||
} else {
|
} else {
|
||||||
max_z = 1;
|
max_z = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { z: max_z };
|
return { board: _.extend(a.board, { z: max_z }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -377,15 +384,15 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a);}.bind(this));
|
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; });
|
var min_z = _.min(overlapping,function(a){ return (a.board?a.board.z:0); });
|
||||||
if (min_z.board) {
|
if (min_z.board) {
|
||||||
min_z = min_z.z - 1;
|
min_z = min_z.board.z - 1;
|
||||||
} else {
|
} else {
|
||||||
min_z = 0;
|
min_z = 0;
|
||||||
}
|
}
|
||||||
var my_z = _.max(this.selected_artifacts(),function(a){ return a.z; });
|
var my_z = _.max(this.selected_artifacts(),function(a){ (a.board?a.board.z:0); });
|
||||||
if (my_z.board) {
|
if (my_z.board) {
|
||||||
my_z = my_z.z - 1;
|
my_z = my_z.board.z - 1;
|
||||||
} else {
|
} else {
|
||||||
my_z = 0;
|
my_z = 0;
|
||||||
}
|
}
|
||||||
@ -393,14 +400,14 @@ var SpacedeckBoardArtifacts = {
|
|||||||
// TODO: move all other items up in this case?
|
// TODO: move all other items up in this case?
|
||||||
if (min_z < 0) {
|
if (min_z < 0) {
|
||||||
this.update_artifacts(overlapping, function(a) {
|
this.update_artifacts(overlapping, function(a) {
|
||||||
return { z: (my_z + a.z + 1) };
|
return { board: _.extend(a.board, { z: (my_z + (a.board?a.board.z:0) + 1) }) };
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { z: min_z };
|
return { board: _.extend(a.board, { z: min_z }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -409,7 +416,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { x: rect.x1 };
|
return { board: _.extend(a.board, { x: rect.x1 }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -418,7 +425,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { y: rect.y1 };
|
return { board: _.extend(a.board, { y: rect.y1 }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -427,7 +434,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { x: rect.x2 - a.w };
|
return { board: _.extend(a.board, { x: rect.x2 - a.board.w }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -436,7 +443,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
|
|
||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { y: rect.y2 - a.h };
|
return { board: _.extend(a.board, { y: rect.y2 - a.board.h }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -446,7 +453,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
var cx = rect.x1 + (rect.x2-rect.x1)/2;
|
var cx = rect.x1 + (rect.x2-rect.x1)/2;
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { x: cx - a.w/2 };
|
return { board: _.extend(a.board, { x: cx - a.board.w/2 }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -456,7 +463,7 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var rect = this.artifact_selection_rect();
|
var rect = this.artifact_selection_rect();
|
||||||
var cy = rect.y1 + (rect.y2-rect.y1)/2;
|
var cy = rect.y1 + (rect.y2-rect.y1)/2;
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { y: cy - a.h/2 };
|
return { board: _.extend(a.board, { y: cy - a.board.h/2 }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -466,11 +473,11 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var arts = this.selected_artifacts();
|
var arts = this.selected_artifacts();
|
||||||
if (arts.length<2) return;
|
if (arts.length<2) return;
|
||||||
|
|
||||||
var totalw = _.reduce(arts, function(sum, a) { return sum + a.w }, 0);
|
var totalw = _.reduce(arts, function(sum, a) { return sum + a.board.w }, 0);
|
||||||
var avgw = totalw / arts.length;
|
var avgw = totalw / arts.length;
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { w: avgw };
|
return { board: _.extend(a.board, { w: avgw }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -480,11 +487,11 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var arts = this.selected_artifacts();
|
var arts = this.selected_artifacts();
|
||||||
if (arts.length<2) return;
|
if (arts.length<2) return;
|
||||||
|
|
||||||
var totalh = _.reduce(arts, function(sum, a) { return sum + a.h }, 0);
|
var totalh = _.reduce(arts, function(sum, a) { return sum + a.board.h }, 0);
|
||||||
var avgh = totalh / arts.length;
|
var avgh = totalh / arts.length;
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return { h: avgh };
|
return { board: _.extend(a.board, { h: avgh }) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -499,16 +506,16 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var selected = this.selected_artifacts();
|
var selected = this.selected_artifacts();
|
||||||
if (selected.length<3) return;
|
if (selected.length<3) return;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return a.x });
|
var sorted = _.sortBy(selected, function(a) { return a.board.x });
|
||||||
var startx = sorted[0].x + sorted[0].w/2;
|
var startx = sorted[0].board.x + sorted[0].board.w/2;
|
||||||
var stopx = _.last(sorted).x + _.last(sorted).w/2;
|
var stopx = _.last(sorted).board.x + _.last(sorted).board.w/2;
|
||||||
var step = (stopx-startx)/(sorted.length-1);
|
var step = (stopx-startx)/(sorted.length-1);
|
||||||
|
|
||||||
for (var i=1; i<sorted.length-1; i++) {
|
for (var i=1; i<sorted.length-1; i++) {
|
||||||
var a = sorted[i];
|
var a = sorted[i];
|
||||||
var x = startx + step*i - a.w/2;
|
var x = startx + step*i - a.board.w/2;
|
||||||
this.update_artifacts([a],function(a) {
|
this.update_artifacts([a],function(a) {
|
||||||
return { x: x }
|
return { board: _.extend(a.board, {x: x}) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -519,16 +526,16 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var selected = this.selected_artifacts();
|
var selected = this.selected_artifacts();
|
||||||
if (selected.length<3) return;
|
if (selected.length<3) return;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return a.y });
|
var sorted = _.sortBy(selected, function(a) { return a.board.y });
|
||||||
var starty = sorted[0].y + sorted[0].h/2;
|
var starty = sorted[0].board.y + sorted[0].board.h/2;
|
||||||
var stopy = _.last(sorted).y + _.last(sorted).h/2;
|
var stopy = _.last(sorted).board.y + _.last(sorted).board.h/2;
|
||||||
var step = (stopy-starty)/(sorted.length-1);
|
var step = (stopy-starty)/(sorted.length-1);
|
||||||
|
|
||||||
for (var i=1; i<sorted.length-1; i++) {
|
for (var i=1; i<sorted.length-1; i++) {
|
||||||
var a = sorted[i];
|
var a = sorted[i];
|
||||||
var y = starty + step*i - a.h/2;
|
var y = starty + step*i - a.board.h/2;
|
||||||
this.update_artifacts([a],function(a) {
|
this.update_artifacts([a],function(a) {
|
||||||
return { y: y }
|
return { board: _.extend(a.board, {y: y}) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -539,21 +546,21 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var selected = this.selected_artifacts();
|
var selected = this.selected_artifacts();
|
||||||
if (selected.length<3) return;
|
if (selected.length<3) return;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return a.x });
|
var sorted = _.sortBy(selected, function(a) { return a.board.x });
|
||||||
var startx = sorted[0].x;
|
var startx = sorted[0].board.x;
|
||||||
var stopx = _.last(sorted).x + _.last(sorted).w;
|
var stopx = _.last(sorted).board.x + _.last(sorted).board.w;
|
||||||
var range = stopx - startx;
|
var range = stopx - startx;
|
||||||
var totalw = _.reduce(sorted, function(sum, a) { return sum + a.w }, 0);
|
var totalw = _.reduce(sorted, function(sum, a) { return sum + a.board.w }, 0);
|
||||||
var avgs = (range - totalw) / (sorted.length-1);
|
var avgs = (range - totalw) / (sorted.length-1);
|
||||||
var prevend = startx + sorted[0].w;
|
var prevend = startx + sorted[0].board.w;
|
||||||
|
|
||||||
for (var i=1; i<sorted.length-1; i++) {
|
for (var i=1; i<sorted.length-1; i++) {
|
||||||
var a = sorted[i];
|
var a = sorted[i];
|
||||||
var x = prevend + avgs;
|
var x = prevend + avgs;
|
||||||
this.update_artifacts([a],function(a) {
|
this.update_artifacts([a],function(a) {
|
||||||
return { x: x }
|
return { board: _.extend(a.board, {x: x}) }
|
||||||
});
|
});
|
||||||
prevend = x+a.w;
|
prevend = x+a.board.w;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -563,21 +570,21 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var selected = this.selected_artifacts();
|
var selected = this.selected_artifacts();
|
||||||
if (selected.length<3) return;
|
if (selected.length<3) return;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return a.y });
|
var sorted = _.sortBy(selected, function(a) { return a.board.y });
|
||||||
var starty = sorted[0].y;
|
var starty = sorted[0].board.y;
|
||||||
var stopy = _.last(sorted).y + _.last(sorted).h;
|
var stopy = _.last(sorted).board.y + _.last(sorted).board.h;
|
||||||
var range = stopy - starty;
|
var range = stopy - starty;
|
||||||
var totalh = _.reduce(sorted, function(sum, a) { return sum + a.h }, 0);
|
var totalh = _.reduce(sorted, function(sum, a) { return sum + a.board.h }, 0);
|
||||||
var avgs = (range - totalh) / (sorted.length-1);
|
var avgs = (range - totalh) / (sorted.length-1);
|
||||||
var prevend = starty + sorted[0].h;
|
var prevend = starty + sorted[0].board.h;
|
||||||
|
|
||||||
for (var i=1; i<sorted.length-1; i++) {
|
for (var i=1; i<sorted.length-1; i++) {
|
||||||
var a = sorted[i];
|
var a = sorted[i];
|
||||||
var y = prevend + avgs;
|
var y = prevend + avgs;
|
||||||
this.update_artifacts([a],function(a) {
|
this.update_artifacts([a],function(a) {
|
||||||
return { y: y }
|
return { board: _.extend(a.board, {y: y}) }
|
||||||
});
|
});
|
||||||
prevend = y+a.h;
|
prevend = y+a.board.h;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -587,22 +594,20 @@ var SpacedeckBoardArtifacts = {
|
|||||||
var selected = this.selected_artifacts();
|
var selected = this.selected_artifacts();
|
||||||
if (selected.length<2) return;
|
if (selected.length<2) return;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return a.x+a.y*this.active_space.width }.bind(this));
|
var sorted = _.sortBy(selected, function(a) { return a.board.x+a.board.y*this.active_space.advanced.width }.bind(this));
|
||||||
|
|
||||||
var minx = sorted[0].x;
|
var minx = sorted[0].board.x;
|
||||||
var miny = sorted[0].y;
|
var miny = sorted[0].board.y;
|
||||||
|
|
||||||
var sorted = _.sortBy(selected, function(a) { return -Math.max(a.w,a.h) }.bind(this));
|
var sorted = _.sortBy(selected, function(a) { return -Math.max(a.board.w,a.board.h) }.bind(this));
|
||||||
|
|
||||||
var blocks = [];
|
var blocks = [];
|
||||||
|
|
||||||
var padding = 10;
|
|
||||||
|
|
||||||
for (var i=0; i<sorted.length; i++) {
|
for (var i=0; i<sorted.length; i++) {
|
||||||
var a = sorted[i];
|
var a = sorted[i];
|
||||||
blocks.push({
|
blocks.push({
|
||||||
w: a.w+padding,
|
w: a.board.w,
|
||||||
h: a.h+padding,
|
h: a.board.h,
|
||||||
a: a
|
a: a
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -615,10 +620,10 @@ var SpacedeckBoardArtifacts = {
|
|||||||
if (block.fit) {
|
if (block.fit) {
|
||||||
var a = block.a;
|
var a = block.a;
|
||||||
this.update_artifacts([a],function(a) {
|
this.update_artifacts([a],function(a) {
|
||||||
return {
|
return { board: _.extend(a.board, {
|
||||||
x: minx+block.fit.x,
|
x: minx+block.fit.x,
|
||||||
y: miny+block.fit.y
|
y: miny+block.fit.y
|
||||||
}
|
}) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +170,7 @@ var SpacedeckRoutes = {
|
|||||||
location.href = "/";
|
location.href = "/";
|
||||||
} else {
|
} else {
|
||||||
this.active_view = "account";
|
this.active_view = "account";
|
||||||
|
this.load_subscription();
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
}
|
}
|
||||||
@ -252,6 +253,8 @@ var SpacedeckRoutes = {
|
|||||||
// #hash
|
// #hash
|
||||||
if (event.currentTarget.hash && event.currentTarget.hash.length>1) return;
|
if (event.currentTarget.hash && event.currentTarget.hash.length>1) return;
|
||||||
|
|
||||||
|
console.log("clicked", event.currentTarget.pathname);
|
||||||
|
|
||||||
// external link?
|
// external link?
|
||||||
if (event.currentTarget.host != location.host) return;
|
if (event.currentTarget.host != location.host) return;
|
||||||
|
|
||||||
@ -267,6 +270,35 @@ var SpacedeckRoutes = {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
|
if (location.host!=ENV.webHost) {
|
||||||
|
if (!subdomainTeam) {
|
||||||
|
location.href = ENV.webEndpoint;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if(subdomainTeam.subdomain) {
|
||||||
|
var realHost = (subdomainTeam.subdomain + "." + ENV.webHost);
|
||||||
|
if (location.host != realHost) {
|
||||||
|
location.href = realHost;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
location.href = ENV.webEndpoint;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.logged_in) {
|
||||||
|
if (this.user.team) {
|
||||||
|
if (this.user.team.subdomain && this.user.team.subdomain.length > 0) {
|
||||||
|
var realHost = (this.user.team.subdomain + "." + ENV.webHost);
|
||||||
|
if (location.host != realHost) {
|
||||||
|
location.href = location.protocol + "//" + realHost + location.pathname;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this.internal_route(location.pathname);
|
this.internal_route(location.pathname);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ var SpacedeckSections = {
|
|||||||
active_style: {
|
active_style: {
|
||||||
border_radius: 0,
|
border_radius: 0,
|
||||||
stroke: 0,
|
stroke: 0,
|
||||||
font_family: "Inter",
|
font_family: "Avenir W01",
|
||||||
font_size: 36,
|
font_size: 18,
|
||||||
line_height: 1.5,
|
line_height: 1.5,
|
||||||
letter_spacing: 0,
|
letter_spacing: 0,
|
||||||
|
|
||||||
@ -110,30 +110,18 @@ var SpacedeckSections = {
|
|||||||
color_picker_opacity: 255,
|
color_picker_opacity: 255,
|
||||||
|
|
||||||
swatches: [
|
swatches: [
|
||||||
{id:1, hex:"#ff00ff"},
|
{id:0, hex:"#4a2f7e"},
|
||||||
{id:2, hex:"#ffff00"},
|
{id:1, hex:"#9b59b6"},
|
||||||
{id:3, hex:"#00ffff"},
|
{id:2, hex:"#3498db"},
|
||||||
{id:5, hex:"#ff0000"},
|
{id:3, hex:"#2ecc71"},
|
||||||
{id:6, hex:"#00ff00"},
|
{id:4, hex:"#f1c40f"},
|
||||||
{id:7, hex:"#0000ff"},
|
{id:5, hex:"#e67e22"},
|
||||||
{id:8, hex:"#000000"},
|
{id:6, hex:"#d55c4b"},
|
||||||
{id:9, hex:"#222222"},
|
{id:7, hex:"#6f4021"},
|
||||||
{id:10, hex:"#444444"},
|
{id:8, hex:"#ffffff"},
|
||||||
{id:11, hex:"#888888"},
|
{id:9, hex:"#95a5a6"},
|
||||||
{id:12, hex:"#bbbbbb"},
|
{id:10, hex:"#252525"},
|
||||||
{id:13, hex:"#dddddd"},
|
{id:11, hex:"rgba(0,0,0,0)"},
|
||||||
{id:14, hex:"#ffffff"},
|
|
||||||
|
|
||||||
{id:20, hex:"#4a2f7e"},
|
|
||||||
{id:21, hex:"#9b59b6"},
|
|
||||||
{id:22, hex:"#3498db"},
|
|
||||||
{id:23, hex:"#2ecc71"},
|
|
||||||
{id:24, hex:"#f1c40f"},
|
|
||||||
{id:25, hex:"#e67e22"},
|
|
||||||
{id:26, hex:"#d55c4b"},
|
|
||||||
{id:27, hex:"#6f4021"},
|
|
||||||
{id:29, hex:"#95a5a6"},
|
|
||||||
{id:30, hex:"rgba(0,0,0,0)"},
|
|
||||||
],
|
],
|
||||||
|
|
||||||
swatches_text: [
|
swatches_text: [
|
||||||
@ -148,8 +136,18 @@ var SpacedeckSections = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
fonts: [
|
fonts: [
|
||||||
"Inter",
|
"Arial",
|
||||||
"Courier"
|
"Courier",
|
||||||
|
"Georgia",
|
||||||
|
"Verdana",
|
||||||
|
"Comic Sans MS",
|
||||||
|
"Montserrat",
|
||||||
|
"Lato",
|
||||||
|
"Roboto",
|
||||||
|
"Crimson Text",
|
||||||
|
"EB Garamond",
|
||||||
|
"Vollkorn",
|
||||||
|
"Avenir W01"
|
||||||
],
|
],
|
||||||
|
|
||||||
detected_text_formats: {},
|
detected_text_formats: {},
|
||||||
@ -182,7 +180,7 @@ var SpacedeckSections = {
|
|||||||
toolbar_props_in: false,
|
toolbar_props_in: false,
|
||||||
toolbar_artifacts_x: "-1000px",
|
toolbar_artifacts_x: "-1000px",
|
||||||
toolbar_artifacts_y: "-1000px",
|
toolbar_artifacts_y: "-1000px",
|
||||||
toolbar_artifacts_in: true
|
toolbar_artifacts_in: false
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -371,8 +369,8 @@ var SpacedeckSections = {
|
|||||||
// canvas
|
// canvas
|
||||||
this.$watch('active_style.background_color', function (value, mutation) {
|
this.$watch('active_style.background_color', function (value, mutation) {
|
||||||
|
|
||||||
if (this.active_style.background_color != this.active_space.background_color) {
|
if (this.active_style.background_color != this.active_space.advanced.background_color) {
|
||||||
this.$set("active_space.background_color",this.active_style.background_color);
|
this.$set("active_space.advanced.background_color",this.active_style.background_color);
|
||||||
this.throttled_save_active_space();
|
this.throttled_save_active_space();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +448,7 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
for (var i=0; i<props.length; i++) {
|
for (var i=0; i<props.length; i++) {
|
||||||
var prop = props[i];
|
var prop = props[i];
|
||||||
this.active_style[prop]=a[prop];
|
this.active_style[prop]=a.style[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
@ -459,10 +457,10 @@ var SpacedeckSections = {
|
|||||||
this.active_style.line_height = this.default_style.line_height;
|
this.active_style.line_height = this.default_style.line_height;
|
||||||
this.active_style.letter_spacing = this.default_style.letter_spacing;
|
this.active_style.letter_spacing = this.default_style.letter_spacing;
|
||||||
|
|
||||||
this.active_style.padding_top = a.padding_top || 0;
|
this.active_style.padding_top = a.style.padding_top || 0;
|
||||||
this.active_style.padding_bottom = a.padding_bottom || 0;
|
this.active_style.padding_bottom = a.style.padding_bottom || 0;
|
||||||
this.active_style.padding_left = a.padding_left || 0;
|
this.active_style.padding_left = a.style.padding_left || 0;
|
||||||
this.active_style.padding_right = a.padding_right || 0;
|
this.active_style.padding_right = a.style.padding_right || 0;
|
||||||
|
|
||||||
if (this.active_style.padding_top == this.active_style.padding_bottom) {
|
if (this.active_style.padding_top == this.active_style.padding_bottom) {
|
||||||
this.active_style.padding_vert = this.active_style.padding_top;
|
this.active_style.padding_vert = this.active_style.padding_top;
|
||||||
@ -478,10 +476,10 @@ var SpacedeckSections = {
|
|||||||
this.active_style.padding = this.active_style.padding_top;
|
this.active_style.padding = this.active_style.padding_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.active_style.margin_top = a.margin_top || 0;
|
this.active_style.margin_top = a.style.margin_top || 0;
|
||||||
this.active_style.margin_bottom = a.margin_bottom || 0;
|
this.active_style.margin_bottom = a.style.margin_bottom || 0;
|
||||||
this.active_style.margin_left = a.margin_left || 0;
|
this.active_style.margin_left = a.style.margin_left || 0;
|
||||||
this.active_style.margin_right = a.margin_right || 0;
|
this.active_style.margin_right = a.style.margin_right || 0;
|
||||||
|
|
||||||
if (this.active_style.margin_top == this.active_style.margin_bottom) {
|
if (this.active_style.margin_top == this.active_style.margin_bottom) {
|
||||||
this.active_style.margin_vert = this.active_style.margin_top;
|
this.active_style.margin_vert = this.active_style.margin_top;
|
||||||
@ -760,8 +758,8 @@ var SpacedeckSections = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
resize_minimap: function() {
|
resize_minimap: function() {
|
||||||
if (!this.active_space) return;
|
if (!this.active_space || !this.active_space.advanced) return;
|
||||||
this.minimap_scale = this.active_space.width/100.0;
|
this.minimap_scale = this.active_space.advanced.width/100.0;
|
||||||
},
|
},
|
||||||
|
|
||||||
handle_minimap_mouseup: function(evt) {
|
handle_minimap_mouseup: function(evt) {
|
||||||
@ -923,7 +921,7 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
discover_zones: function() {
|
discover_zones: function() {
|
||||||
this.zones = _.sortBy(_.filter(this.active_space_artifacts, function(a) { return (a.mime=="x-spacedeck/zone") }),
|
this.zones = _.sortBy(_.filter(this.active_space_artifacts, function(a) { return (a.mime=="x-spacedeck/zone") }),
|
||||||
function(z){return z.order});
|
function(z){return z.style.order});
|
||||||
},
|
},
|
||||||
|
|
||||||
artifact_plaintext: function(a) {
|
artifact_plaintext: function(a) {
|
||||||
@ -1017,10 +1015,10 @@ var SpacedeckSections = {
|
|||||||
arts = _.filter(arts); // remove any nulls
|
arts = _.filter(arts); // remove any nulls
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x1: parseInt(_.min(arts.map(function(a){return ((!a || !a.x)?0:a.x)}))),
|
x1: parseInt(_.min(arts.map(function(a){return ((!a.board || !a.board.x)?0:a.board.x)}))),
|
||||||
y1: parseInt(_.min(arts.map(function(a){return ((!a || !a.y)?0:a.y)}))),
|
y1: parseInt(_.min(arts.map(function(a){return ((!a.board || !a.board.y)?0:a.board.y)}))),
|
||||||
x2: parseInt(_.max(arts.map(function(a){return (!a?0:a.x+a.w)}))),
|
x2: parseInt(_.max(arts.map(function(a){return (!a.board?0:a.board.x+a.board.w)}))),
|
||||||
y2: parseInt(_.max(arts.map(function(a){return (!a?0:a.y+a.h)})))
|
y2: parseInt(_.max(arts.map(function(a){return (!a.board?0:a.board.y+a.board.h)})))
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1059,7 +1057,7 @@ var SpacedeckSections = {
|
|||||||
this.toolbar_props_x = pp.x+"px";
|
this.toolbar_props_x = pp.x+"px";
|
||||||
this.toolbar_props_y = pp.y+"px";
|
this.toolbar_props_y = pp.y+"px";
|
||||||
|
|
||||||
//this.hide_toolbar_artifacts();
|
this.hide_toolbar_artifacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selection_metrics.x1 = sr.x1;
|
this.selection_metrics.x1 = sr.x1;
|
||||||
@ -1078,7 +1076,7 @@ var SpacedeckSections = {
|
|||||||
this.selection_metrics.count=arts.length;
|
this.selection_metrics.count=arts.length;
|
||||||
this.selection_metrics.scribble_selection = false;
|
this.selection_metrics.scribble_selection = false;
|
||||||
if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") {
|
if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") {
|
||||||
if (arts[0].shape == "scribble") {
|
if (arts[0].style.shape == "scribble") {
|
||||||
this.selection_metrics.scribble_selection = true;
|
this.selection_metrics.scribble_selection = true;
|
||||||
}
|
}
|
||||||
this.selection_metrics.vector_points = arts[0].control_points;
|
this.selection_metrics.vector_points = arts[0].control_points;
|
||||||
@ -1114,8 +1112,8 @@ var SpacedeckSections = {
|
|||||||
fixup_space_size: function() {
|
fixup_space_size: function() {
|
||||||
if (!this.active_space) return;
|
if (!this.active_space) return;
|
||||||
|
|
||||||
this.active_space.width =Math.max(this.active_space.width, window.innerWidth);
|
this.active_space.advanced.width =Math.max(this.active_space.advanced.width, window.innerWidth);
|
||||||
this.active_space.height=Math.max(this.active_space.height, window.innerHeight);
|
this.active_space.advanced.height=Math.max(this.active_space.advanced.height, window.innerHeight);
|
||||||
},
|
},
|
||||||
|
|
||||||
end_transaction: function() {
|
end_transaction: function() {
|
||||||
@ -1127,16 +1125,13 @@ var SpacedeckSections = {
|
|||||||
var er = this.enclosing_rect(this.active_space_artifacts);
|
var er = this.enclosing_rect(this.active_space_artifacts);
|
||||||
if (!er) return;
|
if (!er) return;
|
||||||
|
|
||||||
// resize space
|
this.active_space.advanced.width =Math.max(er.x2+100, window.innerWidth);
|
||||||
this.active_space.width =Math.max((parseInt(er.x2/window.innerWidth)+2)*window.innerWidth, window.innerWidth);
|
this.active_space.advanced.height=Math.max(er.y2+100, window.innerHeight);
|
||||||
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.advanced.width ||
|
||||||
|
this._last_bounds_height != this.active_space.advanced.height) {
|
||||||
if (this._last_bounds_width != this.active_space.width ||
|
this._last_bounds_width = this.active_space.advanced.width;
|
||||||
this._last_bounds_height != this.active_space.height) {
|
this._last_bounds_height = this.active_space.advanced.height;
|
||||||
this._last_bounds_width = this.active_space.width;
|
|
||||||
this._last_bounds_height = this.active_space.height;
|
|
||||||
|
|
||||||
save_space(this.active_space);
|
save_space(this.active_space);
|
||||||
}
|
}
|
||||||
@ -1219,7 +1214,7 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
// this is a bit hacky, but might be the smartest place to do it
|
// this is a bit hacky, but might be the smartest place to do it
|
||||||
if (a.view && a.view.vector_svg) {
|
if (a.view && a.view.vector_svg) {
|
||||||
a.shape_svg = a.view.vector_svg;
|
a.style.shape_svg = a.view.vector_svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.artifact_save_queue[a._id] = a;
|
window.artifact_save_queue[a._id] = a;
|
||||||
@ -1334,7 +1329,7 @@ var SpacedeckSections = {
|
|||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
var c = {};
|
var c = {};
|
||||||
|
|
||||||
if (a[prop] != val) {
|
if (c[prop] != val) {
|
||||||
//console.log("set_artifact_prop: ",c,val);
|
//console.log("set_artifact_prop: ",c,val);
|
||||||
c[prop]=val;
|
c[prop]=val;
|
||||||
return c;
|
return c;
|
||||||
@ -1348,11 +1343,11 @@ var SpacedeckSections = {
|
|||||||
this.begin_transaction();
|
this.begin_transaction();
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
var c = {};
|
var c = {style: a.style||{}};
|
||||||
|
|
||||||
if (a[prop] != val) {
|
if (c.style[prop] != val) {
|
||||||
//console.log("set_artifact_style_prop: ",c,val);
|
//console.log("set_artifact_style_prop: ",c,val);
|
||||||
c[prop]=val;
|
c.style[prop]=val;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1424,7 +1419,7 @@ var SpacedeckSections = {
|
|||||||
if (this.selected_artifacts().length!=1 && this.opened_dialog!="background") return;
|
if (this.selected_artifacts().length!=1 && this.opened_dialog!="background") return;
|
||||||
|
|
||||||
if (this.opened_dialog=="background") {
|
if (this.opened_dialog=="background") {
|
||||||
this.active_style[this.color_picker_target] = this.active_space.background_color;
|
this.active_style[this.color_picker_target] = this.active_space.advanced.background_color;
|
||||||
} else {
|
} else {
|
||||||
if (!this.active_style[this.color_picker_target]) {
|
if (!this.active_style[this.color_picker_target]) {
|
||||||
this.active_style[this.color_picker_target] = this.default_style[this.color_picker_target];
|
this.active_style[this.color_picker_target] = this.default_style[this.color_picker_target];
|
||||||
@ -1483,8 +1478,10 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return {
|
return {
|
||||||
x: a.x+dx,
|
board: _.extend(a.board, {
|
||||||
y: a.y+dy
|
x: a.board.x+dx,
|
||||||
|
y: a.board.y+dy
|
||||||
|
})
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -1492,7 +1489,7 @@ var SpacedeckSections = {
|
|||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
highest_z: function() {
|
highest_z: function() {
|
||||||
var z = _.max(this.active_space_artifacts.map(function(a){return a.z||0}));
|
var z = _.max(this.active_space_artifacts.map(function(a){return a.board.z||0}));
|
||||||
if (z<0) z=0;
|
if (z<0) z=0;
|
||||||
if (z>999) z=999;
|
if (z>999) z=999;
|
||||||
return z;
|
return z;
|
||||||
@ -1549,7 +1546,7 @@ var SpacedeckSections = {
|
|||||||
add_artifact: function (space, item_type, url, evt) {
|
add_artifact: function (space, item_type, url, evt) {
|
||||||
this.active_tool = "pointer";
|
this.active_tool = "pointer";
|
||||||
this.mouse_state = "idle";
|
this.mouse_state = "idle";
|
||||||
//this.hide_toolbar_artifacts();
|
this.hide_toolbar_artifacts();
|
||||||
|
|
||||||
if (!url && (item_type == 'image' || item_type == 'video' || item_type == 'embed')) {
|
if (!url && (item_type == 'image' || item_type == 'video' || item_type == 'embed')) {
|
||||||
url = prompt("URL?");
|
url = prompt("URL?");
|
||||||
@ -1577,18 +1574,20 @@ var SpacedeckSections = {
|
|||||||
payload_thumbnail_web_uri: url || null,
|
payload_thumbnail_web_uri: url || null,
|
||||||
space_id: space._id,
|
space_id: space._id,
|
||||||
|
|
||||||
|
style: {
|
||||||
order: this.active_space_artifacts.length+1,
|
order: this.active_space_artifacts.length+1,
|
||||||
valign: "middle",
|
valign: "middle",
|
||||||
align: "center"
|
align: "center"
|
||||||
//fill_color: "#f8f8f8"
|
//fill_color: "#f8f8f8"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mimes[item_type] == "text/html") {
|
if (mimes[item_type] == "text/html") {
|
||||||
new_item.padding_left = 10;
|
new_item.style.padding_left = 10;
|
||||||
new_item.padding_top = 10;
|
new_item.style.padding_top = 10;
|
||||||
new_item.padding_right = 10;
|
new_item.style.padding_right = 10;
|
||||||
new_item.padding_bottom = 10;
|
new_item.style.padding_bottom = 10;
|
||||||
new_item.fill_color = "rgba(255,255,255,1)";
|
new_item.style.fill_color = "rgba(255,255,255,1)";
|
||||||
new_item.description = "<p>Text</p>";
|
new_item.description = "<p>Text</p>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1601,11 +1600,13 @@ var SpacedeckSections = {
|
|||||||
z = point.z;
|
z = point.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_item.x = parseInt(point.x);
|
new_item.board = {
|
||||||
new_item.y = parseInt(point.y);
|
x: parseInt(point.x),
|
||||||
new_item.z = z;
|
y: parseInt(point.y),
|
||||||
new_item.w = w;
|
w: w,
|
||||||
new_item.h = h;
|
h: h,
|
||||||
|
z: z
|
||||||
|
};
|
||||||
|
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
new_item.editor_name = this.guest_nickname;
|
new_item.editor_name = this.guest_nickname;
|
||||||
@ -1664,7 +1665,7 @@ var SpacedeckSections = {
|
|||||||
for (var i=0; i<new_zones.length; i++) {
|
for (var i=0; i<new_zones.length; i++) {
|
||||||
if (new_zones[i]) {
|
if (new_zones[i]) {
|
||||||
if (!new_zones[i].style) new_zones[i].style = {};
|
if (!new_zones[i].style) new_zones[i].style = {};
|
||||||
new_zones[i].order = i;
|
new_zones[i].style.order = i;
|
||||||
save_artifact(new_zones[i]);
|
save_artifact(new_zones[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1678,7 +1679,7 @@ var SpacedeckSections = {
|
|||||||
for (var i=0; i<new_zones.length; i++) {
|
for (var i=0; i<new_zones.length; i++) {
|
||||||
if (new_zones[i]) {
|
if (new_zones[i]) {
|
||||||
if (!new_zones[i].style) new_zones[i].style = {};
|
if (!new_zones[i].style) new_zones[i].style = {};
|
||||||
new_zones[i].order = i;
|
new_zones[i].style.order = i;
|
||||||
save_artifact(new_zones[i]);
|
save_artifact(new_zones[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1694,13 +1695,17 @@ var SpacedeckSections = {
|
|||||||
space_id: this.active_space._id,
|
space_id: this.active_space._id,
|
||||||
mime: "x-spacedeck/zone",
|
mime: "x-spacedeck/zone",
|
||||||
description: "Zone "+(this.zones.length+1),
|
description: "Zone "+(this.zones.length+1),
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
w: w,
|
w: w,
|
||||||
h: h,
|
h: h,
|
||||||
z: 0,
|
z: 0
|
||||||
|
},
|
||||||
|
style: {
|
||||||
valign: "middle",
|
valign: "middle",
|
||||||
align: "center"
|
align: "center"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
@ -1729,19 +1734,23 @@ var SpacedeckSections = {
|
|||||||
var a = {
|
var a = {
|
||||||
space_id: this.active_space._id,
|
space_id: this.active_space._id,
|
||||||
mime: "x-spacedeck/shape",
|
mime: "x-spacedeck/shape",
|
||||||
description: "",
|
description: "Text",
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
z: point.z,
|
z: point.z,
|
||||||
w: w,
|
w: w,
|
||||||
h: h,
|
h: h
|
||||||
|
},
|
||||||
|
style: {
|
||||||
stroke_color: "#ffffff",
|
stroke_color: "#ffffff",
|
||||||
text_color: "#ffffff",
|
text_color: "#ffffff",
|
||||||
stroke: 0,
|
stroke: 0,
|
||||||
fill_color: "#000000",
|
fill_color: "#000000",
|
||||||
shape: shape_type,
|
shape: shape_type,
|
||||||
valign: "middle",
|
valign: "middle",
|
||||||
align: "center",
|
align: "center"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
@ -1794,6 +1803,8 @@ var SpacedeckSections = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.hide_toolbar_artifacts();
|
||||||
|
|
||||||
// 1. create placeholder artifact
|
// 1. create placeholder artifact
|
||||||
var w=300,h=150;
|
var w=300,h=150;
|
||||||
var fill="transparent";
|
var fill="transparent";
|
||||||
@ -1818,14 +1829,18 @@ var SpacedeckSections = {
|
|||||||
state: "uploading",
|
state: "uploading",
|
||||||
payload_thumbnail_medium_uri: null,
|
payload_thumbnail_medium_uri: null,
|
||||||
payload_thumbnail_web_uri: null,
|
payload_thumbnail_web_uri: null,
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
w: w,
|
w: w,
|
||||||
h: h,
|
h: h,
|
||||||
z: point.z,
|
z: point.z
|
||||||
|
},
|
||||||
|
style: {
|
||||||
order: this.active_space_artifacts.length+1,
|
order: this.active_space_artifacts.length+1,
|
||||||
fill_color: fill
|
fill_color: fill
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.update_board_artifact_viewmodel(a);
|
this.update_board_artifact_viewmodel(a);
|
||||||
|
|
||||||
@ -1849,11 +1864,7 @@ var SpacedeckSections = {
|
|||||||
a.payload_thumbnail_big_uri = updated_a.payload_thumbnail_big_uri;
|
a.payload_thumbnail_big_uri = updated_a.payload_thumbnail_big_uri;
|
||||||
a.payload_alternatives = updated_a.payload_alternatives;
|
a.payload_alternatives = updated_a.payload_alternatives;
|
||||||
a.mime = updated_a.mime;
|
a.mime = updated_a.mime;
|
||||||
a.x = updated_a.x;
|
a.board = updated_a.board;
|
||||||
a.y = updated_a.y;
|
|
||||||
a.w = updated_a.w;
|
|
||||||
a.h = updated_a.h;
|
|
||||||
a.z = updated_a.z;
|
|
||||||
a.state = updated_a.state;
|
a.state = updated_a.state;
|
||||||
this.update_board_artifact_viewmodel(a);
|
this.update_board_artifact_viewmodel(a);
|
||||||
|
|
||||||
@ -1991,26 +2002,26 @@ var SpacedeckSections = {
|
|||||||
clear_formatting_walk: function(el,cmd,arg1,arg2) {
|
clear_formatting_walk: function(el,cmd,arg1,arg2) {
|
||||||
if (el && el.style) {
|
if (el && el.style) {
|
||||||
if (cmd == "preciseFontSize") {
|
if (cmd == "preciseFontSize") {
|
||||||
el.fontSize = null;
|
el.style.fontSize = null;
|
||||||
} else if (cmd == "letterSpacing") {
|
} else if (cmd == "letterSpacing") {
|
||||||
el.letterSpacing = null;
|
el.style.letterSpacing = null;
|
||||||
} else if (cmd == "lineHeight") {
|
} else if (cmd == "lineHeight") {
|
||||||
el.lineHeight = null;
|
el.style.lineHeight = null;
|
||||||
} else if (cmd == "fontName") {
|
} else if (cmd == "fontName") {
|
||||||
el.fontFamily = null;
|
el.style.fontFamily = null;
|
||||||
} else if (cmd == "fontWeight") {
|
} else if (cmd == "fontWeight") {
|
||||||
el.fontWeight = null;
|
el.style.fontWeight = null;
|
||||||
el.fontStyle = null;
|
el.style.fontStyle = null;
|
||||||
} else if (cmd == "bold") {
|
} else if (cmd == "bold") {
|
||||||
el.fontWeight = null;
|
el.style.fontWeight = null;
|
||||||
} else if (cmd == "italic") {
|
} else if (cmd == "italic") {
|
||||||
el.fontStyle = null;
|
el.style.fontStyle = null;
|
||||||
} else if (cmd == "underline") {
|
} else if (cmd == "underline") {
|
||||||
el.textDecoration = null;
|
el.style.textDecoration = null;
|
||||||
} else if (cmd == "strikeThrough") {
|
} else if (cmd == "strikeThrough") {
|
||||||
el.textDecoration = null;
|
el.style.textDecoration = null;
|
||||||
} else if (cmd == "forecolor") {
|
} else if (cmd == "forecolor") {
|
||||||
el.color = null;
|
el.style.color = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2097,9 +2108,6 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
if (a.description!=dom.innerHTML) {
|
if (a.description!=dom.innerHTML) {
|
||||||
a.description = dom.innerHTML;
|
a.description = dom.innerHTML;
|
||||||
|
|
||||||
console.log("new DOM:",dom.innerHTML);
|
|
||||||
|
|
||||||
this.update_board_artifact_viewmodel(a);
|
this.update_board_artifact_viewmodel(a);
|
||||||
this.queue_artifact_for_save(a);
|
this.queue_artifact_for_save(a);
|
||||||
|
|
||||||
@ -2133,7 +2141,10 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
remove_link_from_selected_artifacts: function() {
|
remove_link_from_selected_artifacts: function() {
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
return {link_uri: ""};
|
var meta = a.meta || {};
|
||||||
|
delete meta.link_uri;
|
||||||
|
|
||||||
|
return {meta: meta};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2149,7 +2160,9 @@ var SpacedeckSections = {
|
|||||||
var insert_link_url = prompt("URL:",def);
|
var insert_link_url = prompt("URL:",def);
|
||||||
|
|
||||||
this.update_selected_artifacts(function(a) {
|
this.update_selected_artifacts(function(a) {
|
||||||
var update = {link_uri: insert_link_url};
|
var meta = a.meta || {};
|
||||||
|
meta.link_uri = insert_link_url;
|
||||||
|
var update = {meta: meta};
|
||||||
|
|
||||||
if (a.payload_uri && a.payload_uri.match("webgrabber")) {
|
if (a.payload_uri && a.payload_uri.match("webgrabber")) {
|
||||||
var enc_uri = encodeURIComponent(btoa(insert_link_url));
|
var enc_uri = encodeURIComponent(btoa(insert_link_url));
|
||||||
@ -2172,10 +2185,11 @@ var SpacedeckSections = {
|
|||||||
delete copy["$index"];
|
delete copy["$index"];
|
||||||
delete copy["_id"];
|
delete copy["_id"];
|
||||||
|
|
||||||
if (dx) copy.x += dx;
|
if (dx) copy.board.x += dx;
|
||||||
if (dy) copy.y += dy;
|
if (dy) copy.board.y += dy;
|
||||||
|
|
||||||
copy.order = this.active_space_artifacts.length+1;
|
if (!copy.style) copy.style = {};
|
||||||
|
copy.style.order = this.active_space_artifacts.length+1;
|
||||||
|
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
copy.editor_name = this.guest_nickname;
|
copy.editor_name = this.guest_nickname;
|
||||||
@ -2296,6 +2310,11 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
if (!pastedText) return;
|
if (!pastedText) return;
|
||||||
|
|
||||||
|
if (!pastedText.match(/<[a-zA-Z]+>/g)) {
|
||||||
|
// crappy heuristic if this is actually HTML
|
||||||
|
pastedText = pastedText.replace(/\n/g,"<br>");
|
||||||
|
}
|
||||||
|
|
||||||
this.insert_embedded_artifact(pastedText);
|
this.insert_embedded_artifact(pastedText);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2315,16 +2334,16 @@ var SpacedeckSections = {
|
|||||||
if (parsed[i].mime) {
|
if (parsed[i].mime) {
|
||||||
var z = this.highest_z()+1;
|
var z = this.highest_z()+1;
|
||||||
if (parsed.length==1) {
|
if (parsed.length==1) {
|
||||||
var w = parsed[i].w;
|
var w = parsed[i].board.w;
|
||||||
var h = parsed[i].h;
|
var h = parsed[i].board.h;
|
||||||
var point = this.find_place_for_item(w,h);
|
var point = this.find_place_for_item(w,h);
|
||||||
parsed[i].x = point.x;
|
parsed[i].board.x = point.x;
|
||||||
parsed[i].y = point.y;
|
parsed[i].board.y = point.y;
|
||||||
parsed[i].z = point.z;
|
parsed[i].board.z = point.z;
|
||||||
} else {
|
} else {
|
||||||
parsed[i].x = parsed[i].x+50;
|
parsed[i].board.x = parsed[i].board.x+50;
|
||||||
parsed[i].y = parsed[i].y+50;
|
parsed[i].board.y = parsed[i].board.y+50;
|
||||||
parsed[i].y = parsed[i].z+z;
|
parsed[i].board.y = parsed[i].board.z+z;
|
||||||
}
|
}
|
||||||
this.clone_artifact(parsed[i], 0,0, function(a) {
|
this.clone_artifact(parsed[i], 0,0, function(a) {
|
||||||
this.multi_select([a]);
|
this.multi_select([a]);
|
||||||
@ -2342,6 +2361,34 @@ var SpacedeckSections = {
|
|||||||
this.create_artifact_via_embed_url(text);
|
this.create_artifact_via_embed_url(text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var new_item = {
|
||||||
|
mime: "text/html",
|
||||||
|
description: text.replace("\n", "<br />"),
|
||||||
|
title: "",
|
||||||
|
space_id: space._id
|
||||||
|
};
|
||||||
|
|
||||||
|
var w = 400;
|
||||||
|
var h = 300;
|
||||||
|
var point = this.find_place_for_item(w,h);
|
||||||
|
|
||||||
|
new_item.board = {
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
z: point.z
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.guest_nickname) {
|
||||||
|
new_item.editor_name = this.guest_nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_artifact(new_item, function(saved_item) {
|
||||||
|
this.update_board_artifact_viewmodel(saved_item);
|
||||||
|
this.active_space_artifacts.push(saved_item);
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
create_artifact_via_embed_url: function(url) {
|
create_artifact_via_embed_url: function(url) {
|
||||||
@ -2355,13 +2402,17 @@ var SpacedeckSections = {
|
|||||||
mime: "image/png",
|
mime: "image/png",
|
||||||
description: url,
|
description: url,
|
||||||
state: "uploading",
|
state: "uploading",
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
w: 200,
|
w: 200,
|
||||||
h: 200,
|
h: 200,
|
||||||
z: z,
|
z: z
|
||||||
|
},
|
||||||
|
style: {
|
||||||
order: this.active_space_artifacts.length
|
order: this.active_space_artifacts.length
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var metadata = parse_link(url)
|
var metadata = parse_link(url)
|
||||||
|
|
||||||
@ -2422,12 +2473,16 @@ var SpacedeckSections = {
|
|||||||
payload_thumbnail_medium_uri: metadata.thumbnail_url,
|
payload_thumbnail_medium_uri: metadata.thumbnail_url,
|
||||||
payload_thumbnail_web_uri: metadata.thumbnail_url,
|
payload_thumbnail_web_uri: metadata.thumbnail_url,
|
||||||
state: "idle",
|
state: "idle",
|
||||||
|
meta: {
|
||||||
title: metadata.title,
|
title: metadata.title,
|
||||||
link_uri: metadata.url || url,
|
link_uri: metadata.url || url
|
||||||
|
},
|
||||||
|
board: {
|
||||||
x: point.x - w/2,
|
x: point.x - w/2,
|
||||||
y: point.y - h/2,
|
y: point.y - h/2,
|
||||||
w: w,
|
w: w,
|
||||||
h: h
|
h: h
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
@ -2500,11 +2555,20 @@ var SpacedeckSections = {
|
|||||||
this.opened_dialog = "none";
|
this.opened_dialog = "none";
|
||||||
|
|
||||||
if (files && files.length) {
|
if (files && files.length) {
|
||||||
|
console.log("file: ",files[0]);
|
||||||
|
|
||||||
for (var i=0; i<files.length; i++) {
|
for (var i=0; i<files.length; i++) {
|
||||||
var file = files[i];
|
var file = files[i];
|
||||||
|
if (file.type === "application/pdf") {
|
||||||
|
var point = {x: 100, y: 100}; //fixme, center upload?
|
||||||
|
this.dropped_point = point;
|
||||||
|
this.pending_pdf_file = file;
|
||||||
|
this.activate_modal('pdfoptions');
|
||||||
|
} else {
|
||||||
this.create_artifact_via_upload(null, file, true);
|
this.create_artifact_via_upload(null, file, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handle_image_file_upload: function(evt) {
|
handle_image_file_upload: function(evt) {
|
||||||
@ -2527,7 +2591,7 @@ var SpacedeckSections = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
remove_section_background: function() {
|
remove_section_background: function() {
|
||||||
this.active_space.background_uri = null;
|
this.active_space.advanced.background_uri = null;
|
||||||
save_space(this.active_space);
|
save_space(this.active_space);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2541,11 +2605,12 @@ var SpacedeckSections = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
hide_toolbar_props: function() {
|
hide_toolbar_props: function() {
|
||||||
// FIXME test
|
this.toolbar_props_in = false;
|
||||||
//this.toolbar_props_in = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
show_toolbar_artifacts: function(x,y) {
|
show_toolbar_artifacts: function(x,y) {
|
||||||
|
this.toolbar_artifacts_x = (x-175)+"px";
|
||||||
|
this.toolbar_artifacts_y = y+"px";
|
||||||
this.toolbar_artifacts_in = true;
|
this.toolbar_artifacts_in = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2555,19 +2620,29 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
start_adding_artifact: function(evt) {
|
start_adding_artifact: function(evt) {
|
||||||
evt = fixup_touches(evt);
|
evt = fixup_touches(evt);
|
||||||
|
|
||||||
|
// toggle
|
||||||
|
if (this.toolbar_artifacts_in) {
|
||||||
|
this.hide_toolbar_artifacts();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.show_toolbar_artifacts(evt.pageX,evt.pageY);
|
||||||
},
|
},
|
||||||
|
|
||||||
start_drawing_scribble: function(evt) {
|
start_drawing_scribble: function(evt) {
|
||||||
|
this.hide_toolbar_artifacts();
|
||||||
this.active_tool = "scribble";
|
this.active_tool = "scribble";
|
||||||
this.opened_dialog = "none";
|
this.opened_dialog = "none";
|
||||||
},
|
},
|
||||||
|
|
||||||
start_drawing_arrow: function(evt) {
|
start_drawing_arrow: function(evt) {
|
||||||
|
this.hide_toolbar_artifacts();
|
||||||
this.active_tool = "arrow";
|
this.active_tool = "arrow";
|
||||||
this.opened_dialog = "none";
|
this.opened_dialog = "none";
|
||||||
},
|
},
|
||||||
|
|
||||||
start_drawing_line: function(evt) {
|
start_drawing_line: function(evt) {
|
||||||
|
this.hide_toolbar_artifacts();
|
||||||
this.active_tool = "line";
|
this.active_tool = "line";
|
||||||
this.opened_dialog = "none";
|
this.opened_dialog = "none";
|
||||||
},
|
},
|
||||||
@ -2577,8 +2652,8 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
this.bounds_zoom = this.viewport_zoom;
|
this.bounds_zoom = this.viewport_zoom;
|
||||||
|
|
||||||
var eff_w = this.active_space.width*this.viewport_zoom;
|
var eff_w = this.active_space.advanced.width*this.viewport_zoom;
|
||||||
var eff_h = this.active_space.height*this.viewport_zoom;
|
var eff_h = this.active_space.advanced.height*this.viewport_zoom;
|
||||||
|
|
||||||
if (window.innerWidth>eff_w) {
|
if (window.innerWidth>eff_w) {
|
||||||
// horizontal centering
|
// horizontal centering
|
||||||
@ -2771,8 +2846,8 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
var el = $("#space")[0];
|
var el = $("#space")[0];
|
||||||
|
|
||||||
var eff_w = this.active_space.width*this.viewport_zoom;
|
var eff_w = this.active_space.advanced.width*this.viewport_zoom;
|
||||||
var eff_h = this.active_space.height*this.viewport_zoom;
|
var eff_h = this.active_space.advanced.height*this.viewport_zoom;
|
||||||
|
|
||||||
var sx = el.scrollLeft;
|
var sx = el.scrollLeft;
|
||||||
var sy = el.scrollTop;
|
var sy = el.scrollTop;
|
||||||
@ -2846,6 +2921,32 @@ var SpacedeckSections = {
|
|||||||
}.bind(this),500);
|
}.bind(this),500);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
approve_pdf_upload: function(evt,approve_pdf_upload, mode){
|
||||||
|
this.close_modal();
|
||||||
|
|
||||||
|
if(mode == "classic"){
|
||||||
|
this.create_artifact_via_upload(evt, this.pending_pdf_file, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode == "grid") {
|
||||||
|
this.global_spinner = true;
|
||||||
|
save_pdf_file(this.active_space, this.dropped_point, this.pending_pdf_file, approve_pdf_upload, function(createdArtifacts){
|
||||||
|
|
||||||
|
this.global_spinner = false;
|
||||||
|
|
||||||
|
_.each(createdArtifacts, function(new_artifact){
|
||||||
|
this.update_board_artifact_viewmodel(new_artifact);
|
||||||
|
this.active_space_artifacts.push(new_artifact)
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
}.bind(this), function(xhr) {
|
||||||
|
this.global_spinner = false;
|
||||||
|
alert("Error PDF ("+xhr.status+")");
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
handle_data_drop: function(evt) {
|
handle_data_drop: function(evt) {
|
||||||
if (this.active_space_role=="viewer") {
|
if (this.active_space_role=="viewer") {
|
||||||
return false;
|
return false;
|
||||||
@ -2858,8 +2959,17 @@ var SpacedeckSections = {
|
|||||||
if (files && files.length) {
|
if (files && files.length) {
|
||||||
for (var i=0; i<files.length; i++) {
|
for (var i=0; i<files.length; i++) {
|
||||||
var file = files[i];
|
var file = files[i];
|
||||||
|
if (file.type === "application/pdf") {
|
||||||
|
var point = this.cursor_point_to_space(evt);
|
||||||
|
this.dropped_point = point;
|
||||||
|
this.pending_pdf_file = file;
|
||||||
|
this.activate_modal('pdfoptions');
|
||||||
|
|
||||||
|
} else {
|
||||||
this.create_artifact_via_upload(evt, file, (files.length>1));
|
this.create_artifact_via_upload(evt, file, (files.length>1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var json = evt.dataTransfer.getData('application/json');
|
var json = evt.dataTransfer.getData('application/json');
|
||||||
|
|
||||||
@ -2870,9 +2980,9 @@ var SpacedeckSections = {
|
|||||||
|
|
||||||
var w = 300;
|
var w = 300;
|
||||||
var h = 200;
|
var h = 200;
|
||||||
if (parsed.board && parsed.w && parsed.h) {
|
if (parsed.board && parsed.board.w && parsed.board.h) {
|
||||||
w = parsed.w;
|
w = parsed.board.w;
|
||||||
h = parsed.h;
|
h = parsed.board.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
var point = this.cursor_point_to_space(evt);
|
var point = this.cursor_point_to_space(evt);
|
||||||
|
@ -18,6 +18,8 @@ var SpacedeckSpaces = {
|
|||||||
active_space_path: [],
|
active_space_path: [],
|
||||||
access_settings_space: null,
|
access_settings_space: null,
|
||||||
access_settings_memberships: [],
|
access_settings_memberships: [],
|
||||||
|
duplicate_folders: [],
|
||||||
|
duplicate_folder_id: "",
|
||||||
pending_pdf_files: [],
|
pending_pdf_files: [],
|
||||||
|
|
||||||
meta_visible: false,
|
meta_visible: false,
|
||||||
@ -69,7 +71,9 @@ var SpacedeckSpaces = {
|
|||||||
methods: {
|
methods: {
|
||||||
search_spaces: function() {
|
search_spaces: function() {
|
||||||
var query = this.folder_spaces_search;
|
var query = this.folder_spaces_search;
|
||||||
|
console.log("search query: ",query);
|
||||||
load_spaces_search(query, function(spaces) {
|
load_spaces_search(query, function(spaces) {
|
||||||
|
console.log("results: ",spaces);
|
||||||
this.active_profile_spaces = spaces;
|
this.active_profile_spaces = spaces;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -81,7 +85,14 @@ var SpacedeckSpaces = {
|
|||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
ask_guestname: function(dft, cb) {
|
ask_guestname: function(dft, cb) {
|
||||||
smoke.prompt(__('what_is_your_name', "Spacedeck") , function(content) {
|
console.log("ask_guestname");
|
||||||
|
|
||||||
|
var team_name = "Spacedeck";
|
||||||
|
|
||||||
|
if(subdomainTeam) {
|
||||||
|
team_name = subdomainTeam.name;
|
||||||
|
}
|
||||||
|
smoke.prompt(__('what_is_your_name', team_name) , function(content) {
|
||||||
if (!content || (content.length === 0)) {
|
if (!content || (content.length === 0)) {
|
||||||
this.ask_guestname(dft, cb);
|
this.ask_guestname(dft, cb);
|
||||||
} else {
|
} else {
|
||||||
@ -108,6 +119,27 @@ var SpacedeckSpaces = {
|
|||||||
space_auth = get_query_param("spaceAuth");
|
space_auth = get_query_param("spaceAuth");
|
||||||
|
|
||||||
var userReady = function() {
|
var userReady = function() {
|
||||||
|
if (get_query_param("embedded")) {
|
||||||
|
this.embedded = true;
|
||||||
|
this.guest_signup_enabled = true;
|
||||||
|
if (get_query_param("publish_cta")) {
|
||||||
|
this.publish_cta = get_query_param("publish_cta");
|
||||||
|
}
|
||||||
|
if (get_query_param("nosocial")) {
|
||||||
|
this.social_bar = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_query_param("confirm") && this.logged_in) {
|
||||||
|
var token = get_query_param("confirm");
|
||||||
|
confirm_user(this.user, token, function() {
|
||||||
|
this.redirect_to("/spaces/"+space_id+"?show_access=1");
|
||||||
|
}.bind(this), function() {
|
||||||
|
alert("An error occured confirming your email with the given token.");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.close_dropdown();
|
this.close_dropdown();
|
||||||
|
|
||||||
this.active_space_loaded = false;
|
this.active_space_loaded = false;
|
||||||
@ -137,7 +169,10 @@ var SpacedeckSpaces = {
|
|||||||
|
|
||||||
this.active_space_role = role || "viewer"; //via req header from backend
|
this.active_space_role = role || "viewer"; //via req header from backend
|
||||||
|
|
||||||
|
this.space_embed_html = "<iframe width=\"1024\" height=\"768\" seamless src=\""+ENV.webEndpoint+"/spaces/"+space._id+"?embedded=1\"></iframe>";
|
||||||
|
|
||||||
if (!is_home) {
|
if (!is_home) {
|
||||||
|
//console.log(space);
|
||||||
load_members(space, function(members) {
|
load_members(space, function(members) {
|
||||||
this.active_space_memberships = members;
|
this.active_space_memberships = members;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
@ -276,6 +311,15 @@ var SpacedeckSpaces = {
|
|||||||
// FIXME
|
// FIXME
|
||||||
this.active_join_link = "";
|
this.active_join_link = "";
|
||||||
this.join_link_role = "viewer";
|
this.join_link_role = "viewer";
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
if (this.active_space_role == "admin") {
|
||||||
|
this.space_info_section="access";
|
||||||
|
} else if (this.active_space_role == "editor") {
|
||||||
|
//this.space_info_section="versions";
|
||||||
|
} else {
|
||||||
|
this.space_info_section="info";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.bind(this), function(xhr) {
|
}.bind(this), function(xhr) {
|
||||||
|
|
||||||
@ -304,7 +348,7 @@ var SpacedeckSpaces = {
|
|||||||
userReady();
|
userReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.user && space_auth) {
|
if (space_auth) {
|
||||||
if (this.guest_nickname) {
|
if (this.guest_nickname) {
|
||||||
userReady();
|
userReady();
|
||||||
} else {
|
} else {
|
||||||
@ -596,10 +640,13 @@ var SpacedeckSpaces = {
|
|||||||
this.download_space_as_pdf(this.active_space);
|
this.download_space_as_pdf(this.active_space);
|
||||||
}else if (e == "ZIP"){
|
}else if (e == "ZIP"){
|
||||||
this.download_space_as_zip(this.active_space);
|
this.download_space_as_zip(this.active_space);
|
||||||
|
}else if (e == "TXT"){
|
||||||
|
this.download_space_as_list(this.active_space);
|
||||||
}
|
}
|
||||||
}.bind(this), {
|
}.bind(this), {
|
||||||
button_1: "PDF",
|
button_1: "PDF",
|
||||||
button_2: "ZIP",
|
button_2: "ZIP",
|
||||||
|
button_3: "TXT",
|
||||||
button_cancel:__("cancel")
|
button_cancel:__("cancel")
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -639,6 +686,47 @@ var SpacedeckSpaces = {
|
|||||||
location.href = "/api/spaces/" + space._id + "/list";
|
location.href = "/api/spaces/" + space._id + "/list";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
duplicate_space_into_folder: function() {
|
||||||
|
load_writable_folders( function(folders){
|
||||||
|
this.duplicate_folders = _.sortBy(folders, function (folder) { return folder.name; });
|
||||||
|
}.bind(this), function(xhr) {
|
||||||
|
console.error(xhr);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
duplicate_folder_confirm: function() {
|
||||||
|
var folderId = this.duplicate_folder_id;
|
||||||
|
var idx = _.findIndex(this.duplicate_folders, function(s) { return s._id == folderId;});
|
||||||
|
if (idx<0) idx = 0;
|
||||||
|
var folder = this.duplicate_folders[idx];
|
||||||
|
console.log("df f",folder);
|
||||||
|
if (!folder) return;
|
||||||
|
|
||||||
|
duplicate_space(this.active_space, folder._id, function(new_space) {
|
||||||
|
|
||||||
|
this.duplicate_folders = [];
|
||||||
|
this.duplicate_folder = null;
|
||||||
|
|
||||||
|
smoke.quiz(__("duplicate_success", this.active_space.name, folder.name), function(e, test){
|
||||||
|
if (e == __("goto_space", new_space.name)){
|
||||||
|
this.redirect_to("/spaces/" + new_space._id);
|
||||||
|
}else if (e == __("goto_folder", folder.name)){
|
||||||
|
this.redirect_to("/folders/" + folder._id);
|
||||||
|
}
|
||||||
|
}.bind(this), {
|
||||||
|
button_1: __("goto_space", new_space.name),
|
||||||
|
button_2: __("goto_folder", folder.name),
|
||||||
|
button_cancel:__("stay_here")
|
||||||
|
});
|
||||||
|
|
||||||
|
}.bind(this), function(xhr){
|
||||||
|
|
||||||
|
console.error(xhr);
|
||||||
|
smoke.prompt("error: " + xhr.statusText);
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
toggle_follow_mode: function() {
|
toggle_follow_mode: function() {
|
||||||
this.deselect();
|
this.deselect();
|
||||||
this.follow_mode = !this.follow_mode;
|
this.follow_mode = !this.follow_mode;
|
||||||
@ -744,12 +832,9 @@ var SpacedeckSpaces = {
|
|||||||
this.invite_message = "";
|
this.invite_message = "";
|
||||||
}
|
}
|
||||||
}.bind(this), function(xhr){
|
}.bind(this), function(xhr){
|
||||||
try {
|
|
||||||
var res = JSON.parse(xhr.response);
|
text = JSON.stringify(xhr.responseText);
|
||||||
alert("Error: "+res.error);
|
smoke.alert("Error: "+text);
|
||||||
} catch (e) {
|
|
||||||
console.error(e, xhr);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -757,13 +842,9 @@ var SpacedeckSpaces = {
|
|||||||
update_member: function(space, m, role) {
|
update_member: function(space, m, role) {
|
||||||
m.role = role;
|
m.role = role;
|
||||||
save_membership(space, m, function() {
|
save_membership(space, m, function() {
|
||||||
|
console.log("saved")
|
||||||
}.bind(this), function(xhr) {
|
}.bind(this), function(xhr) {
|
||||||
try {
|
console.error(xhr);
|
||||||
var res = JSON.parse(xhr.response);
|
|
||||||
alert("Error: "+res.error);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e, xhr);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -772,12 +853,7 @@ var SpacedeckSpaces = {
|
|||||||
delete_membership(space, m, function() {
|
delete_membership(space, m, function() {
|
||||||
this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1);
|
this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1);
|
||||||
}.bind(this), function(xhr) {
|
}.bind(this), function(xhr) {
|
||||||
try {
|
console.error(xhr);
|
||||||
var res = JSON.parse(xhr.response);
|
|
||||||
alert("Error: "+res.error);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e, xhr);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -813,6 +889,10 @@ var SpacedeckSpaces = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
emojified_comment: function(comment) {
|
||||||
|
return twemoji.parse(comment);
|
||||||
|
},
|
||||||
|
|
||||||
set_folder_sorting: function(key,reverse) {
|
set_folder_sorting: function(key,reverse) {
|
||||||
this.folder_sorting = key;
|
this.folder_sorting = key;
|
||||||
this.folder_reverse = reverse?-1:1;
|
this.folder_reverse = reverse?-1:1;
|
||||||
|
@ -11,13 +11,11 @@ SpacedeckUsers = {
|
|||||||
login_email: "",
|
login_email: "",
|
||||||
login_password: "",
|
login_password: "",
|
||||||
signup_password: "",
|
signup_password: "",
|
||||||
signup_invite_code: "",
|
|
||||||
signup_password_confirmation: "",
|
signup_password_confirmation: "",
|
||||||
account_remove_error: null,
|
account_remove_error: null,
|
||||||
loading_user: false,
|
loading_user: false,
|
||||||
password_reset_confirm_error: "",
|
password_reset_confirm_error: "",
|
||||||
password_reset_error: "",
|
password_reset_error: ""
|
||||||
|
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
load_user: function(on_success, on_error) {
|
load_user: function(on_success, on_error) {
|
||||||
@ -42,7 +40,23 @@ SpacedeckUsers = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
login_google: function(evt) {
|
||||||
|
this.loading_user = true;
|
||||||
|
|
||||||
|
create_oauthtoken(function(data){
|
||||||
|
this.loading_user = false;
|
||||||
|
location.href = data.url;
|
||||||
|
}, function(xhr){
|
||||||
|
this.loading_user = false;
|
||||||
|
alert("could not get oauth token");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
finalize_login: function(session_token, on_success) {
|
finalize_login: function(session_token, on_success) {
|
||||||
|
if(!window.socket_auth || window.socket_auth == '' || window.socket_auth == 'null') {
|
||||||
|
window.socket_auth = session_token;
|
||||||
|
}
|
||||||
|
|
||||||
this.load_user(function(user) {
|
this.load_user(function(user) {
|
||||||
if (this.invitation_token) {
|
if (this.invitation_token) {
|
||||||
accept_invitation(this.invitation_token, function(memberships){
|
accept_invitation(this.invitation_token, function(memberships){
|
||||||
@ -117,7 +131,7 @@ SpacedeckUsers = {
|
|||||||
signup_guest: function(on_success) {
|
signup_guest: function(on_success) {
|
||||||
},
|
},
|
||||||
|
|
||||||
signup_submit: function($event, name, email, password, password_confirmation, invite_code, on_success) {
|
signup_submit: function($event, name, email, password, password_confirmation, on_success) {
|
||||||
this.creating_user = true;
|
this.creating_user = true;
|
||||||
this.signup_error = null;
|
this.signup_error = null;
|
||||||
|
|
||||||
@ -131,7 +145,7 @@ SpacedeckUsers = {
|
|||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
create_user(name, email, password, password_confirmation, invite_code, function(session) {
|
create_user(name, email, password, password_confirmation, function(session) {
|
||||||
this.creating_user = false;
|
this.creating_user = false;
|
||||||
this.login_submit(email, password, null, on_success);
|
this.login_submit(email, password, null, on_success);
|
||||||
}.bind(this), function(req) {
|
}.bind(this), function(req) {
|
||||||
@ -147,14 +161,15 @@ SpacedeckUsers = {
|
|||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
signup_submit_modal: function($event, name, email, password, password_confirmation, invite_code) {
|
signup_submit_modal: function($event, name, email, password, password_confirmation) {
|
||||||
this.signup_submit($event, name, email, password, password_confirmation, invite_code, function() {
|
this.signup_submit($event, name, email, password, password_confirmation, function() {
|
||||||
alert("Success.");
|
alert("Success.");
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
password_reset_submit: function(evt, email) {
|
password_reset_submit: function(evt, email) {
|
||||||
|
|
||||||
if (evt) {
|
if (evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
@ -188,6 +203,7 @@ SpacedeckUsers = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
password_reset_confirm: function(evt, password, password_confirmation) {
|
password_reset_confirm: function(evt, password, password_confirmation) {
|
||||||
|
|
||||||
if (evt) {
|
if (evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
@ -208,17 +224,15 @@ SpacedeckUsers = {
|
|||||||
|
|
||||||
confirm_password_reset(password, this.reset_token, function(parsed,req) {
|
confirm_password_reset(password, this.reset_token, function(parsed,req) {
|
||||||
if(req.status==201){
|
if(req.status==201){
|
||||||
alert("New password set successfully.");
|
|
||||||
this.active_view = "login";
|
this.active_view = "login";
|
||||||
} else {
|
|
||||||
alert("An unknown error occured.");
|
|
||||||
}
|
}
|
||||||
}.bind(this), function(req) {
|
}.bind(this), function(req) {
|
||||||
if (req.status==404) {
|
if (req.status==404) {
|
||||||
alert("Error: Unknown user.");
|
var msg = "user not found";
|
||||||
} else {
|
} else {
|
||||||
alert("Error: "+req.statusText);
|
var msg = "error: " + req.statusText;
|
||||||
}
|
}
|
||||||
|
this.password_reset_confirm_error = msg;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ function boot_spacedeck() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded",function() {
|
$(document).ready(function(){
|
||||||
window.smoke = smoke;
|
window.smoke = smoke;
|
||||||
window.alert = smoke.alert;
|
window.alert = smoke.alert;
|
||||||
|
|
||||||
|
@ -101,13 +101,11 @@ SpacedeckWebsockets = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.websocket && this.websocket.readyState==1) {
|
if (this.websocket && this.websocket.readyState==1) {
|
||||||
var token = "";
|
|
||||||
if (this.user) token = this.user.token;
|
|
||||||
var auth_params = {
|
var auth_params = {
|
||||||
action: "auth",
|
action: "auth",
|
||||||
editor_auth: space_auth,
|
editor_auth: space_auth,
|
||||||
editor_name: this.guest_nickname,
|
editor_name: this.guest_nickname,
|
||||||
auth_token: token,
|
auth_token: window.socket_auth,
|
||||||
space_id: space._id
|
space_id: space._id
|
||||||
};
|
};
|
||||||
console.log("[websocket] auth space");
|
console.log("[websocket] auth space");
|
||||||
|
@ -80,16 +80,10 @@ function setup_whiteboard_directives() {
|
|||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scope.active_tool == "zoom") return;
|
|
||||||
|
|
||||||
if (evt.which == 2) {
|
|
||||||
// middle mouse button
|
|
||||||
this.handle_mouse_down_space(evt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
|
var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
|
||||||
|
|
||||||
|
if ($scope.active_tool == "zoom") return;
|
||||||
|
|
||||||
if ($scope.active_tool == "eyedrop") {
|
if ($scope.active_tool == "eyedrop") {
|
||||||
var arts = $scope.selected_artifacts();
|
var arts = $scope.selected_artifacts();
|
||||||
if (!$scope.is_selected(a) && arts.length > 0) {
|
if (!$scope.is_selected(a) && arts.length > 0) {
|
||||||
@ -202,9 +196,7 @@ function setup_whiteboard_directives() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
handle_mouse_down_space: function(evt) {
|
handle_mouse_down_space: function(evt) {
|
||||||
if (evt.which != 2) {
|
|
||||||
if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
|
if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
|
||||||
}
|
|
||||||
|
|
||||||
var $scope = this.vm.$root;
|
var $scope = this.vm.$root;
|
||||||
|
|
||||||
@ -222,7 +214,7 @@ function setup_whiteboard_directives() {
|
|||||||
this.deselect();
|
this.deselect();
|
||||||
this.mouse_state = "transform";
|
this.mouse_state = "transform";
|
||||||
$scope.mouse_state = this.mouse_state;
|
$scope.mouse_state = this.mouse_state;
|
||||||
this.start_drawing_note(evt);
|
this.start_adding_note(evt);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if ($scope.active_tool=="arrow") {
|
} else if ($scope.active_tool=="arrow") {
|
||||||
@ -339,7 +331,7 @@ function setup_whiteboard_directives() {
|
|||||||
var $scope = this.vm.$root;
|
var $scope = this.vm.$root;
|
||||||
|
|
||||||
return _.filter($scope.active_space_artifacts, function(a) {
|
return _.filter($scope.active_space_artifacts, function(a) {
|
||||||
return this.rects_intersecting(a, rect);
|
return this.rects_intersecting(a.board, rect);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -447,15 +439,15 @@ function setup_whiteboard_directives() {
|
|||||||
|
|
||||||
dists = $scope.unselected_artifacts().map(function(a){
|
dists = $scope.unselected_artifacts().map(function(a){
|
||||||
|
|
||||||
var r = this.rect_to_points(a);
|
var r = this.rect_to_points(a.board);
|
||||||
|
|
||||||
var xd1 = Math.abs(r[0].x-x);
|
var xd1 = Math.abs(r[0].x-x);
|
||||||
var xd2 = Math.abs(r[1].x-x);
|
var xd2 = Math.abs(r[1].x-x);
|
||||||
var xd3 = Math.abs(r[0].x+a.w/2 - x);
|
var xd3 = Math.abs(r[0].x+a.board.w/2 - x);
|
||||||
|
|
||||||
var yd1 = Math.abs(r[0].y-y);
|
var yd1 = Math.abs(r[0].y-y);
|
||||||
var yd2 = Math.abs(r[2].y-y);
|
var yd2 = Math.abs(r[2].y-y);
|
||||||
var yd3 = Math.abs(r[0].y+a.h/2 - y);
|
var yd3 = Math.abs(r[0].y+a.board.h/2 - y);
|
||||||
|
|
||||||
if (!snap_middle) {
|
if (!snap_middle) {
|
||||||
if (xd2<xd1) {
|
if (xd2<xd1) {
|
||||||
@ -477,10 +469,10 @@ function setup_whiteboard_directives() {
|
|||||||
|
|
||||||
if (snap_middle) {
|
if (snap_middle) {
|
||||||
var xd = xd3;
|
var xd = xd3;
|
||||||
var sx = r[0].x+a.w/2;
|
var sx = r[0].x+a.board.w/2;
|
||||||
|
|
||||||
var yd = yd3;
|
var yd = yd3;
|
||||||
var sy = r[0].y+a.h/2;
|
var sy = r[0].y+a.board.h/2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [[xd,sx],[yd,sy]];
|
return [[xd,sx],[yd,sy]];
|
||||||
@ -500,7 +492,6 @@ function setup_whiteboard_directives() {
|
|||||||
if (!xdists[0] || xdists[0][0]>TOL) {
|
if (!xdists[0] || xdists[0][0]>TOL) {
|
||||||
results.snapx = [0,x]; // distance, coordinate
|
results.snapx = [0,x]; // distance, coordinate
|
||||||
} else {
|
} else {
|
||||||
// FIXME snap rulers are broken
|
|
||||||
//$scope.snap_ruler_x = xdists[0][1];
|
//$scope.snap_ruler_x = xdists[0][1];
|
||||||
}
|
}
|
||||||
if (!ydists[0] || ydists[0][0]>TOL) {
|
if (!ydists[0] || ydists[0][0]>TOL) {
|
||||||
@ -525,41 +516,6 @@ function setup_whiteboard_directives() {
|
|||||||
return point;
|
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 a = {
|
|
||||||
space_id: $scope.active_space._id,
|
|
||||||
mime: "text/html",
|
|
||||||
description: "<p>Text</p>",
|
|
||||||
x: point.x,
|
|
||||||
y: point.y,
|
|
||||||
z: z,
|
|
||||||
w: 64,
|
|
||||||
h: 64,
|
|
||||||
align: "center",
|
|
||||||
valign: "middle",
|
|
||||||
stroke_color: "#000000",
|
|
||||||
fill_color: "rgb(241, 196, 15)",
|
|
||||||
stroke: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.save_artifact(a, function(saved_a) {
|
|
||||||
$scope.update_board_artifact_viewmodel(saved_a);
|
|
||||||
$scope.active_space_artifacts.push(saved_a);
|
|
||||||
$scope.select(evt,a);
|
|
||||||
$scope.transform_ox = 0;
|
|
||||||
$scope.transform_oy = 0;
|
|
||||||
$scope.begin_transaction();
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
start_drawing_scribble: function(evt) {
|
start_drawing_scribble: function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
@ -575,14 +531,18 @@ function setup_whiteboard_directives() {
|
|||||||
mime: "x-spacedeck/vector",
|
mime: "x-spacedeck/vector",
|
||||||
description: "",
|
description: "",
|
||||||
control_points: [{dx:0,dy:0}],
|
control_points: [{dx:0,dy:0}],
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
z: z,
|
z: z,
|
||||||
w: 64,
|
w: 64,
|
||||||
h: 64,
|
h: 64
|
||||||
|
},
|
||||||
|
style: {
|
||||||
stroke_color: "#000000",
|
stroke_color: "#000000",
|
||||||
stroke: 2,
|
stroke: 2,
|
||||||
shape: "scribble"
|
shape: "scribble"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.save_artifact(a, function(saved_a) {
|
$scope.save_artifact(a, function(saved_a) {
|
||||||
@ -612,14 +572,18 @@ function setup_whiteboard_directives() {
|
|||||||
mime: "x-spacedeck/vector",
|
mime: "x-spacedeck/vector",
|
||||||
description: "",
|
description: "",
|
||||||
control_points: [{dx:0,dy:0},{dx:0,dy:0},{dx:0,dy:0}],
|
control_points: [{dx:0,dy:0},{dx:0,dy:0},{dx:0,dy:0}],
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
z: z,
|
z: z,
|
||||||
w: 64,
|
w: 64,
|
||||||
h: 64,
|
h: 64
|
||||||
|
},
|
||||||
|
style: {
|
||||||
stroke_color: "#000000",
|
stroke_color: "#000000",
|
||||||
stroke: 2,
|
stroke: 2,
|
||||||
shape: "arrow"
|
shape: "arrow"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.save_artifact(a, function(saved_a) {
|
$scope.save_artifact(a, function(saved_a) {
|
||||||
@ -648,14 +612,18 @@ function setup_whiteboard_directives() {
|
|||||||
mime: "x-spacedeck/vector",
|
mime: "x-spacedeck/vector",
|
||||||
description: "",
|
description: "",
|
||||||
control_points: [{dx:0,dy:0},{dx:0,dy:0}],
|
control_points: [{dx:0,dy:0},{dx:0,dy:0}],
|
||||||
|
board: {
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
z: z,
|
z: z,
|
||||||
w: 64,
|
w: 64,
|
||||||
h: 64,
|
h: 64
|
||||||
|
},
|
||||||
|
style: {
|
||||||
stroke_color: "#000000",
|
stroke_color: "#000000",
|
||||||
stroke: 2,
|
stroke: 2,
|
||||||
shape: "line"
|
shape: "line"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.save_artifact(a, function(saved_a) {
|
$scope.save_artifact(a, function(saved_a) {
|
||||||
@ -707,11 +675,11 @@ function setup_whiteboard_directives() {
|
|||||||
|
|
||||||
if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) {
|
if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) {
|
||||||
// some types of artifact need a minimum size
|
// some types of artifact need a minimum size
|
||||||
if (ars[i].w<10) {
|
if (ars[i].board.w<10) {
|
||||||
ars[i].w = 10;
|
ars[i].board.w = 10;
|
||||||
}
|
}
|
||||||
if (ars[i].h<10) {
|
if (ars[i].board.h<10) {
|
||||||
ars[i].h = 10;
|
ars[i].board.h = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,7 +703,7 @@ function setup_whiteboard_directives() {
|
|||||||
this.mouse_state = "idle";
|
this.mouse_state = "idle";
|
||||||
$scope.mouse_state = this.mouse_state;
|
$scope.mouse_state = this.mouse_state;
|
||||||
this.lasso = null;
|
this.lasso = null;
|
||||||
//$scope.active_tool = "pointer"; disabled to keep the same tool (eg. "Scribble") after drawing a single line.
|
$scope.active_tool = "pointer";
|
||||||
$scope.end_transaction();
|
$scope.end_transaction();
|
||||||
$scope.show_toolbar_props();
|
$scope.show_toolbar_props();
|
||||||
|
|
||||||
@ -859,8 +827,10 @@ function setup_whiteboard_directives() {
|
|||||||
|
|
||||||
if (old_a) {
|
if (old_a) {
|
||||||
return {
|
return {
|
||||||
x: old_a.x + dx - snap_dx,
|
board: _.extend(a.board, {
|
||||||
y: old_a.y + dy - snap_dy
|
x: old_a.board.x + dx - snap_dx,
|
||||||
|
y: old_a.board.y + dy - snap_dy
|
||||||
|
})
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// deleted?
|
// deleted?
|
||||||
@ -900,19 +870,21 @@ function setup_whiteboard_directives() {
|
|||||||
$scope.update_selected_artifacts(function(a) {
|
$scope.update_selected_artifacts(function(a) {
|
||||||
var old_a = $scope.find_artifact_before_transaction(a);
|
var old_a = $scope.find_artifact_before_transaction(a);
|
||||||
|
|
||||||
var x1 = origin_x + ((old_a.x - origin_x) * scale_x);
|
var x1 = origin_x + ((old_a.board.x - origin_x) * scale_x);
|
||||||
var y1 = origin_y + ((old_a.y - origin_y) * scale_y);
|
var y1 = origin_y + ((old_a.board.y - origin_y) * scale_y);
|
||||||
var x2 = origin_x + (((old_a.x + old_a.w) - origin_x) * scale_x);
|
var x2 = origin_x + (((old_a.board.x + old_a.board.w) - origin_x) * scale_x);
|
||||||
var y2 = origin_y + (((old_a.y + old_a.h) - origin_y) * scale_y);
|
var y2 = origin_y + (((old_a.board.y + old_a.board.h) - origin_y) * scale_y);
|
||||||
|
|
||||||
if (x1>x2) { var t = x1; x1 = x2; x2 = t; }
|
if (x1>x2) { var t = x1; x1 = x2; x2 = t; }
|
||||||
if (y1>y2) { var t = y1; y1 = y2; y2 = t; }
|
if (y1>y2) { var t = y1; y1 = y2; y2 = t; }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
board: _.extend(a.board, {
|
||||||
x: x1,
|
x: x1,
|
||||||
y: y1,
|
y: y1,
|
||||||
w: x2 - x1,
|
w: x2 - x1,
|
||||||
h: y2 - y1
|
h: y2 - y1
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
@ -930,17 +902,18 @@ function setup_whiteboard_directives() {
|
|||||||
var old_a = $scope.find_artifact_before_transaction(a);
|
var old_a = $scope.find_artifact_before_transaction(a);
|
||||||
|
|
||||||
var control_points = _.cloneDeep(old_a.control_points);
|
var control_points = _.cloneDeep(old_a.control_points);
|
||||||
|
var board = _.clone(old_a.board);
|
||||||
var cp = control_points[$scope.selected_control_point_idx];
|
var cp = control_points[$scope.selected_control_point_idx];
|
||||||
|
|
||||||
var snapped = _this.snap_point(old_a.x+cp.dx+dx, old_a.y+cp.dy+dy);
|
var snapped = _this.snap_point(board.x+cp.dx+dx, board.y+cp.dy+dy);
|
||||||
dx = snapped.snapx[1]-(old_a.x+cp.dx);
|
dx = snapped.snapx[1]-(board.x+cp.dx);
|
||||||
dy = snapped.snapy[1]-(old_a.y+cp.dy);
|
dy = snapped.snapy[1]-(board.y+cp.dy);
|
||||||
|
|
||||||
cp.dx += dx;
|
cp.dx += dx;
|
||||||
cp.dy += dy;
|
cp.dy += dy;
|
||||||
|
|
||||||
// special case for arrow's 3rd point
|
// special case for arrow's 3rd point
|
||||||
if (a.shape == "arrow" && $scope.selected_control_point_idx!=2) {
|
if (a.style.shape == "arrow" && $scope.selected_control_point_idx!=2) {
|
||||||
/*control_points[2].dx += dx/2;
|
/*control_points[2].dx += dx/2;
|
||||||
control_points[2].dy += dy/2; */
|
control_points[2].dy += dy/2; */
|
||||||
|
|
||||||
@ -948,7 +921,7 @@ function setup_whiteboard_directives() {
|
|||||||
control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2;
|
control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _this.normalize_control_points(control_points, old_a);
|
return _this.normalize_control_points(control_points, board);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (this.mouse_state == "scribble") {
|
} else if (this.mouse_state == "scribble") {
|
||||||
@ -957,14 +930,16 @@ function setup_whiteboard_directives() {
|
|||||||
var old_a = a;
|
var old_a = a;
|
||||||
|
|
||||||
var control_points = _.cloneDeep(old_a.control_points);
|
var control_points = _.cloneDeep(old_a.control_points);
|
||||||
|
var board = _.clone(old_a.board);
|
||||||
|
|
||||||
var offset = this.offset_point_in_wrapper({x:cursor.x,y:cursor.y});
|
var offset = this.offset_point_in_wrapper({x:cursor.x,y:cursor.y});
|
||||||
|
|
||||||
control_points.push({
|
control_points.push({
|
||||||
dx: offset.x-old_a.x,
|
dx: offset.x-board.x,
|
||||||
dy: offset.y-old_a.y
|
dy: offset.y-board.y
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.normalize_control_points(simplify_scribble_points(control_points), old_a);
|
return this.normalize_control_points(simplify_scribble_points(control_points), board);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var arts = $scope.selected_artifacts();
|
var arts = $scope.selected_artifacts();
|
||||||
@ -984,7 +959,7 @@ function setup_whiteboard_directives() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
normalize_control_points: function(control_points, artifact) {
|
normalize_control_points: function(control_points, board) {
|
||||||
var x1 = _.min(control_points,"dx").dx;
|
var x1 = _.min(control_points,"dx").dx;
|
||||||
var y1 = _.min(control_points,"dy").dy;
|
var y1 = _.min(control_points,"dy").dy;
|
||||||
var x2 = _.max(control_points,"dx").dx;
|
var x2 = _.max(control_points,"dx").dx;
|
||||||
@ -1006,15 +981,19 @@ function setup_whiteboard_directives() {
|
|||||||
var bshiftx = 0;
|
var bshiftx = 0;
|
||||||
var bshifty = 0;
|
var bshifty = 0;
|
||||||
|
|
||||||
if (artifact.w < 0) bshiftx = -artifact.w;
|
if (board.w < 0) bshiftx = -board.w;
|
||||||
if (artifact.h < 0) bshifty = -artifact.h;
|
if (board.h < 0) bshifty = -board.h;
|
||||||
|
|
||||||
return {
|
var shifted_board = {
|
||||||
x: artifact.x + bshiftx - shiftx,
|
x: board.x + bshiftx - shiftx,
|
||||||
y: artifact.y + bshifty - shifty,
|
y: board.y + bshifty - shifty,
|
||||||
w: w,
|
w: w,
|
||||||
h: h,
|
h: h,
|
||||||
z: artifact.z,
|
z: board.z
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
board: shifted_board,
|
||||||
control_points: shifted_cps
|
control_points: shifted_cps
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ function vec2_angle(v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render_vector_drawing(a, padding) {
|
function render_vector_drawing(a, padding) {
|
||||||
var shape = a.shape || "";
|
var shape = a.style.shape || "";
|
||||||
var path = [];
|
var path = [];
|
||||||
var p = a.control_points[0];
|
var p = a.control_points[0];
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ function render_vector_drawing(a, padding) {
|
|||||||
|
|
||||||
var d = "M" + (cps.dx + padding) + "," + (cps.dy + padding) + " Q" + (scaledMiddlePoint.dx + padding) + "," + (scaledMiddlePoint.dy + padding) + " " + (cpe.dx + padding) + "," + (cpe.dy + padding);
|
var d = "M" + (cps.dx + padding) + "," + (cps.dy + padding) + " Q" + (scaledMiddlePoint.dx + padding) + "," + (scaledMiddlePoint.dy + padding) + " " + (cpe.dx + padding) + "," + (cpe.dy + padding);
|
||||||
var tip = "<defs><marker id='ae" + markerId + "' refX=\"0.1\" refY=\"3\" markerWidth=\"3\" markerHeight=\"6\" orient=\"auto\">";
|
var tip = "<defs><marker id='ae" + markerId + "' refX=\"0.1\" refY=\"3\" markerWidth=\"3\" markerHeight=\"6\" orient=\"auto\">";
|
||||||
tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.stroke_color+"\" stroke-width=\"0\"/></marker></defs>";
|
tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.style.stroke_color+"\" stroke-width=\"0\"/></marker></defs>";
|
||||||
var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.stroke + ";' marker-end='url(#ae" + markerId + ")'/>";
|
var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.style.stroke + ";' marker-end='url(#ae" + markerId + ")'/>";
|
||||||
|
|
||||||
return svg;
|
return svg;
|
||||||
}
|
}
|
||||||
@ -237,11 +237,11 @@ function render_vector_rect(xradius,yradius,offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render_vector_shape(a) {
|
function render_vector_shape(a) {
|
||||||
var stroke = parseInt(a.stroke) + 4;
|
var stroke = parseInt(a.style.stroke) + 4;
|
||||||
var offset = stroke / 2;
|
var offset = stroke / 2;
|
||||||
|
|
||||||
var xr = (a.w-stroke) / 2;
|
var xr = (a.board.w-stroke) / 2;
|
||||||
var yr = (a.h-stroke) / 2;
|
var yr = (a.board.h-stroke) / 2;
|
||||||
|
|
||||||
var shape_renderers = {
|
var shape_renderers = {
|
||||||
ellipse: function() { return render_vector_ellipse(xr, yr, offset); },
|
ellipse: function() { return render_vector_ellipse(xr, yr, offset); },
|
||||||
@ -258,7 +258,7 @@ function render_vector_shape(a) {
|
|||||||
cloud: function() { return render_vector_cloud(xr, yr, offset); },
|
cloud: function() { return render_vector_cloud(xr, yr, offset); },
|
||||||
}
|
}
|
||||||
|
|
||||||
var render_func = shape_renderers[a.shape];
|
var render_func = shape_renderers[a.style.shape];
|
||||||
|
|
||||||
if (!render_func) return "";
|
if (!render_func) return "";
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,41 +1,57 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
require('../../models/schema');
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var archiver = require('archiver');
|
||||||
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
|
var gm = require('gm');
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
const db = require('../../models/db');
|
var userMapping = { '_id': 1, 'nickname': 1, 'email': 1};
|
||||||
const Sequelize = require('sequelize');
|
var spaceMapping = { '_id': 1, name: 1};
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
router.get('/:membership_id/accept', function(req, res, next) {
|
router.get('/:membership_id/accept', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
db.Membership.findOne({where:{
|
Membership.findOne({
|
||||||
_id: req.params.membership_id,
|
_id: req.params.membership_id,
|
||||||
code: req.query.code
|
state: "pending",
|
||||||
}, include: ['space']}).then((mem) => {
|
code: req.query.code,
|
||||||
|
user: { "$exists": false }
|
||||||
|
}).populate('space').exec((err,mem) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
if (mem) {
|
if (mem) {
|
||||||
if(!mem.user) {
|
if(!mem.user) {
|
||||||
|
mem.code = null;
|
||||||
mem.state = "active";
|
mem.state = "active";
|
||||||
mem.user_id = req.user._id;
|
mem.user = req.user;
|
||||||
|
|
||||||
mem.save().then(function() {
|
mem.save(function(err){
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
console.log(mem);
|
||||||
res.status(200).json(mem);
|
res.status(200).json(mem);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(200).json(mem);
|
res.status(400).json({"error": "already_used"});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json({"error": "not found"});
|
res.status(404).json({"error": "not found"});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
|
@ -1,72 +1,79 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const db = require('../../models/db');
|
require('../../models/schema');
|
||||||
|
|
||||||
var bcrypt = require('bcryptjs');
|
var bcrypt = require('bcrypt');
|
||||||
var crypto = require('crypto');
|
var crypo = require('crypto');
|
||||||
var URL = require('url').URL;
|
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
router.post('/', function(req, res) {
|
router.post('/', function(req, res) {
|
||||||
var data = req.body;
|
var data = req.body;
|
||||||
if (!data.email || !data.password) {
|
if (data.email && data.password) {
|
||||||
res.status(400).json({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var email = req.body.email.toLowerCase();
|
var email = req.body.email.toLowerCase();
|
||||||
var password = req.body["password"];
|
var password = req.body["password"];
|
||||||
|
|
||||||
db.User.findOne({where: {email: email}})
|
User.find({email: email, account_type: "email"}, (function (err, users) {
|
||||||
.error(err => {
|
if (err) {
|
||||||
res.sendStatus(404);
|
res.status(400).json({"error":"session.users"});
|
||||||
})
|
} else {
|
||||||
.then(user => {
|
|
||||||
if (!user) {
|
if (users.length == 1) {
|
||||||
res.sendStatus(404);
|
var user = users[0];
|
||||||
}
|
|
||||||
else if (bcrypt.compareSync(password, user.password_hash)) {
|
if (bcrypt.compareSync(password, user.password_hash)) {
|
||||||
crypto.randomBytes(48, function(ex, buf) {
|
crypo.randomBytes(48, function(ex, buf) {
|
||||||
var token = buf.toString('hex');
|
var token = buf.toString('hex');
|
||||||
|
|
||||||
var session = {
|
var session = {
|
||||||
user_id: user._id,
|
|
||||||
token: token,
|
token: token,
|
||||||
ip: req.ip,
|
ip: req.ip,
|
||||||
device: "web",
|
device: "web",
|
||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
db.Session.create(session)
|
if (!user.sessions)
|
||||||
.error(err => {
|
user.sessions = [];
|
||||||
console.error("Error creating Session:",err);
|
|
||||||
res.sendStatus(500);
|
user.sessions.push(session);
|
||||||
})
|
|
||||||
.then(() => {
|
user.save(function(err, result) {
|
||||||
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname;
|
// FIXME
|
||||||
res.cookie('sdsession', token, { domain: domain, httpOnly: true });
|
var secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
|
||||||
|
var domain = (process.env.NODE_ENV == "production") ? ".example.org" : "localhost";
|
||||||
|
|
||||||
|
res.cookie('sdsession', token, { domain: domain, httpOnly: true, secure: secure});
|
||||||
res.status(201).json(session);
|
res.status(201).json(session);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
res.status(400).json({});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/current', function(req, res, next) {
|
router.delete('/current', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
var token = req.cookies['sdsession'];
|
var user = req.user;
|
||||||
db.Session.findOne({where: {token: token}})
|
var newSessions = user.sessions.filter( function(session){
|
||||||
.then(session => {
|
return session.token != req.token;
|
||||||
session.destroy();
|
|
||||||
});
|
});
|
||||||
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname;
|
user.sessions = newSessions;
|
||||||
|
user.save(function(err, result) {
|
||||||
|
// FIXME
|
||||||
|
var domain = (process.env.NODE_ENV == "production") ? ".example.org" : "localhost";
|
||||||
res.clearCookie('sdsession', { domain: domain });
|
res.clearCookie('sdsession', { domain: domain });
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
|
require('../../models/schema');
|
||||||
const os = require('os');
|
|
||||||
const db = require('../../models/db');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
var payloadConverter = require('../../helpers/artifact_converter');
|
var payloadConverter = require('../../helpers/artifact_converter');
|
||||||
var redis = require('../../helpers/redis');
|
var redis = require('../../helpers/redis');
|
||||||
@ -14,11 +9,13 @@ var redis = require('../../helpers/redis');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
var archiver = require('archiver');
|
var archiver = require('archiver');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
var gm = require('gm');
|
var gm = require('gm');
|
||||||
|
|
||||||
@ -49,27 +46,22 @@ var roleMapping = {
|
|||||||
// ARTIFACTS
|
// ARTIFACTS
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
db.Artifact.findAll({where: {
|
Artifact.find({
|
||||||
space_id: req.space._id
|
space_id: req.space._id
|
||||||
}}).then(artifacts => {
|
}).exec((err, artifacts) => {
|
||||||
async.map(artifacts, (a, cb) => {
|
async.map(artifacts, (a, cb) => {
|
||||||
db.unpackArtifact(a);
|
a = a.toObject();
|
||||||
|
|
||||||
if (a.user_id) {
|
if (a.user_id) {
|
||||||
// FIXME JOIN
|
User.findOne({
|
||||||
/*User.findOne({where: {
|
|
||||||
"_id": a.user_id
|
"_id": a.user_id
|
||||||
}}).select({
|
}).select({
|
||||||
"_id": 1,
|
"_id": 1,
|
||||||
"nickname": 1,
|
"nickname": 1,
|
||||||
"email": 1
|
"email": 1
|
||||||
}).exec((err, user) => {
|
}).exec((err, user) => {
|
||||||
if (user) {
|
|
||||||
a['user'] = user.toObject();
|
a['user'] = user.toObject();
|
||||||
}
|
|
||||||
cb(err, a);
|
cb(err, a);
|
||||||
});*/
|
});
|
||||||
cb(null, a);
|
|
||||||
} else {
|
} else {
|
||||||
cb(null, a);
|
cb(null, a);
|
||||||
}
|
}
|
||||||
@ -87,8 +79,9 @@ router.post('/', function(req, res, next) {
|
|||||||
|
|
||||||
attrs['space_id'] = req.space._id;
|
attrs['space_id'] = req.space._id;
|
||||||
|
|
||||||
var artifact = attrs;
|
var artifact = new Artifact(attrs);
|
||||||
artifact._id = uuidv4();
|
|
||||||
|
artifact.created_from_ip = req['real_ip'];
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
artifact.user_id = req.user._id;
|
artifact.user_id = req.user._id;
|
||||||
@ -97,18 +90,23 @@ router.post('/', function(req, res, next) {
|
|||||||
artifact.last_update_editor_name = req.editor_name;
|
artifact.last_update_editor_name = req.editor_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.packArtifact(artifact);
|
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
|
||||||
|
artifact.save(function(err) {
|
||||||
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
|
if (err) res.status(400).json(err);
|
||||||
db.Artifact.create(artifact).then(() => {
|
else {
|
||||||
//if (err) res.status(400).json(err);
|
Space.update({
|
||||||
db.unpackArtifact(artifact);
|
_id: req.space._id
|
||||||
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
|
}, {
|
||||||
|
"$set": {
|
||||||
|
updated_at: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
res.distributeCreate("Artifact", artifact);
|
res.distributeCreate("Artifact", artifact);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(401).json({
|
res.status(401).json({
|
||||||
"error": "Access denied"
|
"error": "no access"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -118,26 +116,30 @@ router.post('/:artifact_id/payload', function(req, res, next) {
|
|||||||
var a = req.artifact;
|
var a = req.artifact;
|
||||||
|
|
||||||
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
|
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
|
||||||
|
var localFilePath = "/tmp/" + fileName;
|
||||||
var localFilePath = os.tmpdir() + "/" + fileName;
|
|
||||||
var writeStream = fs.createWriteStream(localFilePath);
|
var writeStream = fs.createWriteStream(localFilePath);
|
||||||
var stream = req.pipe(writeStream);
|
var stream = req.pipe(writeStream);
|
||||||
|
|
||||||
var progressCallback = function(progressMsg) {
|
var progress_callback = function(progress_msg) {
|
||||||
a.description = progressMsg.toString();
|
a.description = progress_msg;
|
||||||
db.packArtifact(a);
|
|
||||||
a.save();
|
a.save();
|
||||||
redis.sendMessage("update", "Artifact", a, req.channelId);
|
redis.sendMessage("update", a, a.toJSON(), req.channelId);
|
||||||
};
|
};
|
||||||
|
|
||||||
stream.on('finish', function() {
|
stream.on('finish', function() {
|
||||||
payloadConverter.convert(a, fileName, localFilePath, function(error, artifact) {
|
payloadConverter.convert(a, fileName, localFilePath, function(error, artifact) {
|
||||||
if (error) res.status(400).json(error);
|
if (error) res.status(400).json(error);
|
||||||
else {
|
else {
|
||||||
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
|
Space.update({
|
||||||
|
_id: req.space._id
|
||||||
|
}, {
|
||||||
|
"$set": {
|
||||||
|
updated_at: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
res.distributeUpdate("Artifact", artifact);
|
res.distributeUpdate("Artifact", artifact);
|
||||||
}
|
}
|
||||||
}, progressCallback);
|
}, progress_callback);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(401).json({
|
res.status(401).json({
|
||||||
@ -158,23 +160,41 @@ router.put('/:artifact_id', function(req, res, next) {
|
|||||||
newAttr.last_update_editor_name = req.editor_name;
|
newAttr.last_update_editor_name = req.editor_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.packArtifact(newAttr);
|
Artifact.findOneAndUpdate({
|
||||||
|
|
||||||
db.Artifact.update(newAttr, { where: {
|
|
||||||
"_id": a._id
|
"_id": a._id
|
||||||
}}).then(rows => {
|
}, {
|
||||||
db.unpackArtifact(newAttr);
|
"$set": newAttr
|
||||||
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
|
}, {
|
||||||
newAttr._id = a._id;
|
"new": true
|
||||||
res.distributeUpdate("Artifact", newAttr);
|
}, function(err, artifact) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
Space.update({
|
||||||
|
_id: req.space._id
|
||||||
|
}, {
|
||||||
|
"$set": {
|
||||||
|
updated_at: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.distributeUpdate("Artifact", artifact);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:artifact_id', function(req, res, next) {
|
router.delete('/:artifact_id', function(req, res, next) {
|
||||||
var artifact = req.artifact;
|
var artifact = req.artifact;
|
||||||
db.Artifact.destroy({where: { "_id": artifact._id}}).then(() => {
|
artifact.remove(function(err) {
|
||||||
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
Space.update({
|
||||||
|
_id: req.space._id
|
||||||
|
}, {
|
||||||
|
"$set": {
|
||||||
|
updated_at: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
res.distributeDelete("Artifact", artifact);
|
res.distributeDelete("Artifact", artifact);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
require('../../models/db');
|
require('../../models/schema');
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
var gm = require('gm');
|
var gm = require('gm');
|
||||||
|
|
||||||
@ -38,12 +40,6 @@ var roleMapping = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
router.get('/', function(req, res, next) {
|
router.get('/', function(req, res, next) {
|
||||||
|
|
||||||
res.status(200).json([]);
|
|
||||||
return;
|
|
||||||
|
|
||||||
// FIXME TODO
|
|
||||||
|
|
||||||
var showActionForSpaces = function(err, spaceIds) {
|
var showActionForSpaces = function(err, spaceIds) {
|
||||||
var userMapping = {
|
var userMapping = {
|
||||||
'_id': 1,
|
'_id': 1,
|
||||||
@ -138,6 +134,7 @@ router.get('/', function(req, res, next) {
|
|||||||
"$exists": 1
|
"$exists": 1
|
||||||
}
|
}
|
||||||
}).populate("space").exec(function(err, memberships) {
|
}).populate("space").exec(function(err, memberships) {
|
||||||
|
|
||||||
async.map(memberships, function(membership, memcb) {
|
async.map(memberships, function(membership, memcb) {
|
||||||
Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) {
|
Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) {
|
||||||
cb(null, spaces.map(function(s) {
|
cb(null, spaces.map(function(s) {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const db = require('../../models/db');
|
require('../../models/schema');
|
||||||
|
|
||||||
|
var redis = require('../../helpers/redis');
|
||||||
var mailer = require('../../helpers/mailer');
|
var mailer = require('../../helpers/mailer');
|
||||||
var uploader = require('../../helpers/uploader');
|
var uploader = require('../../helpers/uploader');
|
||||||
var space_render = require('../../helpers/space-render');
|
var space_render = require('../../helpers/space-render');
|
||||||
@ -11,11 +12,13 @@ var async = require('async');
|
|||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
var archiver = require('archiver');
|
var archiver = require('archiver');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
var gm = require('gm');
|
var gm = require('gm');
|
||||||
var sanitizeHtml = require('sanitize-html');
|
var sanitizeHtml = require('sanitize-html');
|
||||||
@ -46,17 +49,26 @@ var roleMapping = {
|
|||||||
|
|
||||||
router.get('/png', function(req, res, next) {
|
router.get('/png', function(req, res, next) {
|
||||||
var triggered = new Date();
|
var triggered = new Date();
|
||||||
|
|
||||||
var s3_filename = "s" + req.space._id + "/" + "thumb_" + triggered.getTime() + ".jpg";
|
var s3_filename = "s" + req.space._id + "/" + "thumb_" + triggered.getTime() + ".jpg";
|
||||||
|
|
||||||
if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) {
|
if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) {
|
||||||
db.Space.update({ thumbnail_updated_at: triggered }, {where : {"_id": req.space._id }});
|
|
||||||
|
|
||||||
phantom.takeScreenshot(req.space, "png", function(local_path) {
|
Space.update({
|
||||||
|
"_id": req.space._id
|
||||||
|
}, {
|
||||||
|
"$set": {
|
||||||
|
thumbnail_updated_at: triggered
|
||||||
|
}
|
||||||
|
}, function(a, b, c) {});
|
||||||
|
|
||||||
|
phantom.takeScreenshot(req.space, "png",
|
||||||
|
function(local_path) {
|
||||||
var localResizedFilePath = local_path + ".thumb.jpg";
|
var localResizedFilePath = local_path + ".thumb.jpg";
|
||||||
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
|
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("[space screenshot] resize error: ", err);
|
console.error("screenshot resize error: ", err);
|
||||||
res.status(500).send("Error taking screenshot.");
|
res.status(500).send("Error taking screenshot.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -64,28 +76,35 @@ router.get('/png', function(req, res, next) {
|
|||||||
uploader.uploadFile(s3_filename, "image/jpeg", localResizedFilePath, function(err, thumbnailUrl) {
|
uploader.uploadFile(s3_filename, "image/jpeg", localResizedFilePath, function(err, thumbnailUrl) {
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error("[space screenshot] upload error. filename: " + s3_filename + " details: ", err);
|
console.error("screenshot s3 upload error. filename: " + s3_filename + " details: ", err);
|
||||||
res.status(500).send("Error uploading screenshot.");
|
res.status(500).send("Error uploading screenshot.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldUrl = req.space.thumbnail_url;
|
var oldUrl = req.space.thumbnail_url;
|
||||||
|
|
||||||
db.Space.update({ thumbnail_url: thumbnailUrl }, {where : {"_id": req.space._id }}).then(() => {
|
Space.update({
|
||||||
|
"_id": req.space._id
|
||||||
|
}, {
|
||||||
|
"$set": {
|
||||||
|
thumbnail_url: thumbnailUrl
|
||||||
|
}
|
||||||
|
}, function(a, b, c) {
|
||||||
res.redirect(thumbnailUrl);
|
res.redirect(thumbnailUrl);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (oldUrl) {
|
if (oldUrl) {
|
||||||
var oldPath = url.parse(oldUrl).pathname;
|
var oldPath = url.parse(oldUrl).pathname;
|
||||||
uploader.removeFile(oldPath, function(err, res) {});
|
uploader.removeFile(oldPath, function(err, res) {});
|
||||||
}
|
}
|
||||||
fs.unlinkSync(local_path);
|
fs.unlink(local_path);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(localResizedFilePath);
|
fs.unlink(localResizedFilePath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@ -94,7 +113,7 @@ router.get('/png', function(req, res, next) {
|
|||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
// on_error
|
// on_error
|
||||||
console.error("[space screenshot] could not create screenshot for space " + req.space_id);
|
console.error("phantom could not create screenshot for space " + req.space_id);
|
||||||
res.status(404).send("Not found");
|
res.status(404).send("Not found");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -106,6 +125,77 @@ function make_export_filename(space, extension) {
|
|||||||
return space.name.replace(/[^\w]/g, '') + "-" + space._id + "-" + moment().format("YYYYMMDD-HH-mm-ss") + "." + extension;
|
return space.name.replace(/[^\w]/g, '') + "-" + space._id + "-" + moment().format("YYYYMMDD-HH-mm-ss") + "." + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.get('/list', function(req, res, next) {
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
if (req.spaceRole == "admin" || req.spaceRole == "editor") {
|
||||||
|
|
||||||
|
if (req.space.space_type == "space") {
|
||||||
|
Artifact.find({
|
||||||
|
space_id: req.space._id
|
||||||
|
}).exec(function(err, artifacts) {
|
||||||
|
async.map(artifacts, function(a, cb) {
|
||||||
|
if (a.user_id) {
|
||||||
|
User.findOne({
|
||||||
|
"_id": a.user_id
|
||||||
|
}).exec(function(err, user) {
|
||||||
|
a.user = user;
|
||||||
|
|
||||||
|
if (a.last_update_user_id) {
|
||||||
|
User.findOne({
|
||||||
|
"_id": a.last_update_user_id
|
||||||
|
}).exec(function(err, updateUser) {
|
||||||
|
a.update_user = updateUser;
|
||||||
|
cb(null, a);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, a);
|
||||||
|
}
|
||||||
|
}, function(err, mappedArtifacts) {
|
||||||
|
|
||||||
|
req.space.artifacts = mappedArtifacts.map(function(a) {
|
||||||
|
a.description = sanitizeHtml(a.description, {
|
||||||
|
allowedTags: [],
|
||||||
|
allowedAttributes: []
|
||||||
|
});
|
||||||
|
|
||||||
|
if (a.payload_uri) {
|
||||||
|
var parsed = url.parse(a.payload_uri);
|
||||||
|
var fileName = path.basename(parsed.pathname) || "file.bin";
|
||||||
|
a.filename = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.render('artifact_list', {
|
||||||
|
space: req.space
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Space.getRecursiveSubspacesForSpace(req.space, (err, subspaces) => {
|
||||||
|
res.render('space_list', {
|
||||||
|
subspaces: subspaces.map((s) => {
|
||||||
|
s.ae_link = config.endpoint + '/s/' + s.edit_hash + (s.edit_slug ? ('-'+s.edit_slug) : '')
|
||||||
|
return s;
|
||||||
|
}),
|
||||||
|
space: req.space
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.sendStatus(403);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.sendStatus(403);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/pdf', function(req, res, next) {
|
router.get('/pdf', function(req, res, next) {
|
||||||
var s3_filename = make_export_filename(req.space, "pdf");
|
var s3_filename = make_export_filename(req.space, "pdf");
|
||||||
|
|
||||||
@ -239,13 +329,36 @@ router.get('/zip', function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.get('/html', function(req, res) {
|
router.get('/html', function(req, res) {
|
||||||
db.Artifact.findAll({where: {
|
Artifact.find({
|
||||||
space_id: req.space._id
|
space_id: req.space._id
|
||||||
}}).then(function(artifacts) {
|
}, function(err, artifacts) {
|
||||||
var space = req.space;
|
var space = req.space;
|
||||||
res.send(space_render.render_space_as_html(space, artifacts));
|
res.send(space_render.render_space_as_html(space, artifacts));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
router.get('/path', (req, res) => {
|
||||||
|
// build up a breadcrumb trail (path)
|
||||||
|
var path = [];
|
||||||
|
var buildPath = (space) => {
|
||||||
|
if (space.parent_space_id) {
|
||||||
|
Space.findOne({
|
||||||
|
"_id": space.parent_space_id
|
||||||
|
}, (err, parentSpace) => {
|
||||||
|
if (space._id == parentSpace._id) {
|
||||||
|
console.log("error: circular parent reference for space " + space._id);
|
||||||
|
res.send("error: circular reference");
|
||||||
|
} else {
|
||||||
|
path.push(parentSpace);
|
||||||
|
buildPath(parentSpace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// reached the top
|
||||||
|
res.json(path.reverse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildPath(req.space);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
@ -1,31 +1,57 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const db = require('../../models/db');
|
require('../../models/schema');
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
var redis = require('../../helpers/redis');
|
var redis = require('../../helpers/redis');
|
||||||
var mailer = require('../../helpers/mailer');
|
var mailer = require('../../helpers/mailer');
|
||||||
|
var uploader = require('../../helpers/uploader');
|
||||||
|
var space_render = require('../../helpers/space-render');
|
||||||
|
var phantom = require('../../helpers/phantom');
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
|
var archiver = require('archiver');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var glob = require('glob');
|
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
|
var glob = require('glob');
|
||||||
|
var gm = require('gm');
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router({mergeParams: true});
|
var router = express.Router({mergeParams: true});
|
||||||
|
|
||||||
|
// JSON MAPPINGS
|
||||||
|
var userMapping = {
|
||||||
|
_id: 1,
|
||||||
|
nickname: 1,
|
||||||
|
email: 1,
|
||||||
|
avatar_thumb_uri: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var spaceMapping = {
|
||||||
|
_id: 1,
|
||||||
|
name: 1,
|
||||||
|
thumbnail_url: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var roleMapping = {
|
||||||
|
"none": 0,
|
||||||
|
"viewer": 1,
|
||||||
|
"editor": 2,
|
||||||
|
"admin": 3
|
||||||
|
}
|
||||||
|
|
||||||
router.get('/', function(req, res, next) {
|
router.get('/', function(req, res, next) {
|
||||||
db.Membership
|
Membership
|
||||||
.findAll({where: {
|
.find({
|
||||||
space_id: req.space._id
|
space: req.space._id
|
||||||
}, include: ['user']})
|
})
|
||||||
.then(memberships => {
|
.populate("user")
|
||||||
|
.exec(function(err, memberships) {
|
||||||
res.status(200).json(memberships);
|
res.status(200).json(memberships);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -33,28 +59,26 @@ router.get('/', function(req, res, next) {
|
|||||||
router.post('/', function(req, res, next) {
|
router.post('/', function(req, res, next) {
|
||||||
if (req.spaceRole == "admin") {
|
if (req.spaceRole == "admin") {
|
||||||
var attrs = req.body;
|
var attrs = req.body;
|
||||||
attrs.space_id = req.space._id;
|
attrs['space'] = req.space._id;
|
||||||
attrs.state = "pending";
|
attrs['state'] = "pending";
|
||||||
attrs._id = uuidv4();
|
var membership = new Membership(attrs);
|
||||||
var membership = attrs;
|
|
||||||
|
|
||||||
var msg = attrs.personal_message;
|
var msg = attrs.personal_message;
|
||||||
|
|
||||||
if (membership.email_invited != req.user.email) {
|
if (membership.email_invited != req.user.email) {
|
||||||
db.User.findOne({where:{
|
User.findOne({
|
||||||
"email": membership.email_invited
|
"email": membership.email_invited
|
||||||
}}).then(function(user) {
|
}, function(err, user) {
|
||||||
|
|
||||||
// existing user? then immediately activate membership
|
|
||||||
if (user) {
|
if (user) {
|
||||||
membership.user_id = user._id;
|
membership.user = user;
|
||||||
membership.state = "active";
|
membership.state = "active";
|
||||||
} else {
|
} else {
|
||||||
// if not, invite via email and invite code
|
|
||||||
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
|
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Membership.create(membership).then(function() {
|
membership.save(function(err) {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
|
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
@ -80,19 +104,20 @@ router.post('/', function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json(membership);
|
res.status(201).json(membership);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
"error": "This email is already included in the Space memberships."
|
"error": "user already in space"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
"error": "Only administrators can do that."
|
"error": "not_permitted"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -100,20 +125,19 @@ router.post('/', function(req, res, next) {
|
|||||||
router.put('/:membership_id', function(req, res, next) {
|
router.put('/:membership_id', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
if (req.spaceRole == "admin") {
|
if (req.spaceRole == "admin") {
|
||||||
db.Membership.findOne({ where: {
|
Membership.findOne({
|
||||||
_id: req.params.membership_id
|
_id: req.params.membership_id
|
||||||
}}).then(function(mem) {
|
}, function(err, mem) {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
if (mem) {
|
if (mem) {
|
||||||
// is the user trying to change their own role?
|
|
||||||
if (mem.user_id == req.user._id) {
|
|
||||||
res.status(400).json({
|
|
||||||
"error": "Cannot change your own role."
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var attrs = req.body;
|
var attrs = req.body;
|
||||||
mem.role = attrs.role;
|
mem.role = attrs.role;
|
||||||
mem.save(function() {
|
mem.save(function(err) {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
res.status(201).json(mem);
|
res.status(201).json(mem);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,25 +151,21 @@ router.put('/:membership_id', function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:membership_id', function(req, res, next) {
|
router.delete('/:membership_id', function(req, res, next) {
|
||||||
if (req.user && req.spaceRole == 'admin') {
|
if (req.user) {
|
||||||
db.Membership.count({ where: {
|
Membership.findOne({
|
||||||
space_id: req.space._id,
|
|
||||||
role: "admin"
|
|
||||||
}}).then(function(adminCount) {
|
|
||||||
db.Membership.findOne({ where: {
|
|
||||||
_id: req.params.membership_id
|
_id: req.params.membership_id
|
||||||
}}).then(function(mem) {
|
}, function(err, mem) {
|
||||||
// deleting an admin? need at least 1
|
if (err) res.sendStatus(400);
|
||||||
if (mem.role != "admin" || adminCount > 1) {
|
else {
|
||||||
mem.destroy().then(function() {
|
mem.remove(function(err) {
|
||||||
res.sendStatus(204);
|
if (err) {
|
||||||
});
|
res.status(400).json(err);
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({
|
// FIXME might need to delete the user?
|
||||||
"error": "Space needs at least one administrator."
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const db = require('../../models/db');
|
require('../../models/schema');
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
var redis = require('../../helpers/redis');
|
var redis = require('../../helpers/redis');
|
||||||
var mailer = require('../../helpers/mailer');
|
var mailer = require('../../helpers/mailer');
|
||||||
@ -14,12 +11,15 @@ var phantom = require('../../helpers/phantom');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
var archiver = require('archiver');
|
var archiver = require('archiver');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
|
var gm = require('gm');
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router({mergeParams: true});
|
var router = express.Router({mergeParams: true});
|
||||||
@ -49,44 +49,90 @@ var roleMapping = {
|
|||||||
// MESSAGES
|
// MESSAGES
|
||||||
|
|
||||||
router.get('/', function(req, res, next) {
|
router.get('/', function(req, res, next) {
|
||||||
db.Message.findAll({where:{
|
Message.find({
|
||||||
space_id: req.space._id
|
space: req.space._id
|
||||||
}, include: ['user']})
|
}).populate('user', userMapping).exec(function(err, messages) {
|
||||||
.then(function(messages) {
|
|
||||||
res.status(200).json(messages);
|
res.status(200).json(messages);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/', function(req, res, next) {
|
router.post('/', function(req, res, next) {
|
||||||
var attrs = req.body;
|
var attrs = req.body;
|
||||||
attrs.space_id = req.space._id;
|
attrs.space = req.space;
|
||||||
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
attrs.user = req.user;
|
attrs.user = req.user;
|
||||||
attrs.user_id = req.user._id;
|
|
||||||
} else {
|
} else {
|
||||||
attrs.user = null;
|
attrs.user = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = attrs;
|
var msg = new Message(attrs);
|
||||||
msg._id = uuidv4();
|
msg.save(function(err) {
|
||||||
|
if (err) res.status(400).json(erra);
|
||||||
db.Message.create(msg).then(function() {
|
else {
|
||||||
if (msg.message.length <= 1) return;
|
if (msg.message.length <= 1) return;
|
||||||
// TODO reimplement notifications
|
|
||||||
|
Membership
|
||||||
|
.find({
|
||||||
|
space: req.space,
|
||||||
|
user: {
|
||||||
|
"$exists": true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.populate('user')
|
||||||
|
.exec(function(err, memberships) {
|
||||||
|
var users = memberships.map(function(m) {
|
||||||
|
return m.user;
|
||||||
|
});
|
||||||
|
users.forEach((user) => {
|
||||||
|
if (user.preferences.email_notifications) {
|
||||||
|
redis.isOnlineInSpace(user, req.space, function(err, online) {
|
||||||
|
if (!online) {
|
||||||
|
var nickname = msg.editor_name;
|
||||||
|
if (req.user) {
|
||||||
|
nickname = req.user.nickname;
|
||||||
|
}
|
||||||
|
mailer.sendMail(
|
||||||
|
user.email,
|
||||||
|
req.i18n.__("space_message_subject", req.space.name),
|
||||||
|
req.i18n.__("space_message_body", nickname, req.space.name), {
|
||||||
|
message: msg.message,
|
||||||
|
action: {
|
||||||
|
link: config.endpoint + "/spaces/" + req.space._id.toString(),
|
||||||
|
name: req.i18n.__("open")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("not sending message to user: is online.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("not sending message to user: is disabled notifications.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
res.distributeCreate("Message", msg);
|
res.distributeCreate("Message", msg);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:message_id', function(req, res, next) {
|
router.delete('/:message_id', function(req, res, next) {
|
||||||
db.Message.findOne({where:{
|
Message.findOne({
|
||||||
"_id": req.params.message_id
|
"_id": req.params.message_id
|
||||||
}}).then(function(msg) {
|
}, function(err, msg) {
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
} else {
|
} else {
|
||||||
msg.destroy().then(function() {
|
msg.remove(function(err) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
if (msg) {
|
||||||
res.distributeDelete("Message", msg);
|
res.distributeDelete("Message", msg);
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const os = require('os');
|
require('../../models/schema');
|
||||||
const db = require('../../models/db');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
var redis = require('../../helpers/redis');
|
var redis = require('../../helpers/redis');
|
||||||
var mailer = require('../../helpers/mailer');
|
var mailer = require('../../helpers/mailer');
|
||||||
@ -18,10 +14,13 @@ var slug = require('slug');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var _ = require("underscore");
|
var _ = require("underscore");
|
||||||
|
var mongoose = require("mongoose");
|
||||||
|
var archiver = require('archiver');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var url = require("url");
|
var url = require("url");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var qr = require('qr-image');
|
||||||
var glob = require('glob');
|
var glob = require('glob');
|
||||||
var gm = require('gm');
|
var gm = require('gm');
|
||||||
const exec = require('child_process');
|
const exec = require('child_process');
|
||||||
@ -48,49 +47,119 @@ router.get('/', function(req, res, next) {
|
|||||||
error: "auth required"
|
error: "auth required"
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (req.query.search) {
|
if (req.query.writablefolders) {
|
||||||
db.Membership.findAll({where:{
|
Membership.find({
|
||||||
user_id: req.user._id
|
user: req.user._id
|
||||||
}}).then(memberships => {
|
}, (err, memberships) => {
|
||||||
// search for spaces
|
|
||||||
|
|
||||||
var validMemberships = memberships.filter(function(m) {
|
var validMemberships = memberships.filter((m) => {
|
||||||
if (!m.space_id || (m.space_id == "undefined"))
|
if (!m.space || (m.space == "undefined"))
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
return true;
|
return mongoose.Types.ObjectId.isValid(m.space.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
var editorMemberships = validMemberships.filter((m) => {
|
||||||
|
return (m.role == "editor") || (m.role == "admin")
|
||||||
|
});
|
||||||
|
|
||||||
|
var spaceIds = editorMemberships.map(function(m) {
|
||||||
|
return new mongoose.Types.ObjectId(m.space);
|
||||||
|
});
|
||||||
|
|
||||||
|
var q = {
|
||||||
|
"space_type": "folder",
|
||||||
|
"$or": [{
|
||||||
|
"creator": req.user._id
|
||||||
|
}, {
|
||||||
|
"_id": {
|
||||||
|
"$in": spaceIds
|
||||||
|
},
|
||||||
|
"creator": {
|
||||||
|
"$ne": req.user._id
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
Space
|
||||||
|
.find(q)
|
||||||
|
.populate('creator', userMapping)
|
||||||
|
.exec(function(err, spaces) {
|
||||||
|
if (err) console.error(err);
|
||||||
|
var updatedSpaces = spaces.map(function(s) {
|
||||||
|
var spaceObj = s.toObject();
|
||||||
|
return spaceObj;
|
||||||
|
});
|
||||||
|
|
||||||
|
async.map(spaces, (space, cb) => {
|
||||||
|
Space.getRecursiveSubspacesForSpace(space, (err, spaces) => {
|
||||||
|
var allSpaces = spaces;
|
||||||
|
cb(err, allSpaces);
|
||||||
|
})
|
||||||
|
}, (err, spaces) => {
|
||||||
|
|
||||||
|
var allSpaces = _.flatten(spaces);
|
||||||
|
|
||||||
|
var onlyFolders = _.filter(allSpaces, (s) => {
|
||||||
|
return s.space_type == "folder";
|
||||||
|
})
|
||||||
|
var uniqueFolders = _.unique(onlyFolders, (s) => {
|
||||||
|
return s._id.toString();
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json(uniqueFolders);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (req.query.search) {
|
||||||
|
|
||||||
|
Membership.find({
|
||||||
|
user: req.user._id
|
||||||
|
}, function(err, memberships) {
|
||||||
|
|
||||||
|
var validMemberships = memberships.filter(function(m) {
|
||||||
|
if (!m.space || (m.space == "undefined"))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return mongoose.Types.ObjectId.isValid(m.space.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
var spaceIds = validMemberships.map(function(m) {
|
var spaceIds = validMemberships.map(function(m) {
|
||||||
return m.space_id;
|
return new mongoose.Types.ObjectId(m.space);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO FIXME port
|
var q = {
|
||||||
var q = { where: {
|
"$or": [{"creator": req.user._id},
|
||||||
[Op.or]: [{"creator_id": req.user._id},
|
{"_id": {"$in": spaceIds}},
|
||||||
{"_id": {[Op.in]: spaceIds}},
|
{"parent_space_id": {"$in": spaceIds}}],
|
||||||
{"parent_space_id": {[Op.in]: spaceIds}}],
|
name: new RegExp(req.query.search, "i")
|
||||||
name: {[Op.like]: "%"+req.query.search+"%"}
|
};
|
||||||
}, include: ['creator']};
|
|
||||||
|
|
||||||
db.Space
|
Space
|
||||||
.findAll(q)
|
.find(q)
|
||||||
.then(function(spaces) {
|
.populate('creator', userMapping)
|
||||||
|
.exec(function(err, spaces) {
|
||||||
|
if (err) console.error(err);
|
||||||
|
var updatedSpaces = spaces.map(function(s) {
|
||||||
|
var spaceObj = s.toObject();
|
||||||
|
return spaceObj;
|
||||||
|
});
|
||||||
res.status(200).json(spaces);
|
res.status(200).json(spaces);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
|
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
|
||||||
// list spaces in a folder
|
|
||||||
|
|
||||||
db.Space
|
Space
|
||||||
.findOne({where: {
|
.findOne({
|
||||||
_id: req.query.parent_space_id
|
_id: req.query.parent_space_id
|
||||||
}})
|
})
|
||||||
//.populate('creator', userMapping)
|
.populate('creator', userMapping)
|
||||||
.then(function(space) {
|
.exec(function(err, space) {
|
||||||
if (space) {
|
if (space) {
|
||||||
db.getUserRoleInSpace(space, req.user, function(role) {
|
Space.roleInSpace(space, req.user, function(err, role) {
|
||||||
|
|
||||||
if (role == "none") {
|
if (role == "none") {
|
||||||
if(space.access_mode == "public") {
|
if(space.access_mode == "public") {
|
||||||
role = "viewer";
|
role = "viewer";
|
||||||
@ -98,11 +167,12 @@ router.get('/', function(req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role != "none") {
|
if (role != "none") {
|
||||||
db.Space
|
Space
|
||||||
.findAll({where:{
|
.find({
|
||||||
parent_space_id: req.query.parent_space_id
|
parent_space_id: req.query.parent_space_id
|
||||||
}, include:['creator']})
|
})
|
||||||
.then(function(spaces) {
|
.populate('creator', userMapping)
|
||||||
|
.exec(function(err, spaces) {
|
||||||
res.status(200).json(spaces);
|
res.status(200).json(spaces);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -115,42 +185,41 @@ router.get('/', function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// list home folder and spaces/folders that the user is a member of
|
Membership.find({
|
||||||
|
user: req.user._id
|
||||||
db.Membership.findAll({ where: {
|
}, function(err, memberships) {
|
||||||
user_id: req.user._id
|
|
||||||
}}).then(memberships => {
|
|
||||||
if (!memberships) memberships = [];
|
|
||||||
|
|
||||||
var validMemberships = memberships.filter(function(m) {
|
var validMemberships = memberships.filter(function(m) {
|
||||||
if (!m.space_id || (m.space_id == "undefined"))
|
if (!m.space || (m.space == "undefined"))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
else
|
||||||
|
return mongoose.Types.ObjectId.isValid(m.space.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
var spaceIds = validMemberships.map(function(m) {
|
var spaceIds = validMemberships.map(function(m) {
|
||||||
return m.space_id;
|
return new mongoose.Types.ObjectId(m.space);
|
||||||
});
|
});
|
||||||
|
|
||||||
var q = {
|
var q = {
|
||||||
[Op.or]: [{
|
"$or": [{
|
||||||
"creator_id": req.user._id,
|
"creator": req.user._id,
|
||||||
"parent_space_id": req.user.home_folder_id
|
"parent_space_id": req.user.home_folder_id
|
||||||
}, {
|
}, {
|
||||||
"_id": {
|
"_id": {
|
||||||
[Op.in]: spaceIds
|
"$in": spaceIds
|
||||||
},
|
},
|
||||||
"creator_id": {
|
"creator": {
|
||||||
[Op.ne]: req.user._id
|
"$ne": req.user._id
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
db.Space
|
Space
|
||||||
.findAll({where: q, include: ['creator']})
|
.find(q)
|
||||||
.then(function(spaces) {
|
.populate('creator', userMapping)
|
||||||
|
.exec(function(err, spaces) {
|
||||||
|
if (err) console.error(err);
|
||||||
var updatedSpaces = spaces.map(function(s) {
|
var updatedSpaces = spaces.map(function(s) {
|
||||||
var spaceObj = db.spaceToObject(s);
|
var spaceObj = s.toObject();
|
||||||
return spaceObj;
|
return spaceObj;
|
||||||
});
|
});
|
||||||
res.status(200).json(spaces);
|
res.status(200).json(spaces);
|
||||||
@ -160,47 +229,47 @@ router.get('/', function(req, res, next) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create a space
|
|
||||||
router.post('/', function(req, res, next) {
|
router.post('/', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
var attrs = req.body;
|
var attrs = req.body;
|
||||||
|
|
||||||
var createSpace = () => {
|
var createSpace = () => {
|
||||||
attrs._id = uuidv4();
|
|
||||||
attrs.creator_id = req.user._id;
|
attrs.creator = req.user;
|
||||||
attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
|
attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
|
||||||
attrs.edit_slug = slug(attrs.name);
|
attrs.edit_slug = slug(attrs.name);
|
||||||
attrs.access_mode = "private";
|
|
||||||
|
|
||||||
db.Space.create(attrs).then(createdSpace => {
|
var space = new Space(attrs);
|
||||||
res.status(201).json(createdSpace);
|
space.save(function(err, createdSpace) {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
// create initial admin membership
|
else {
|
||||||
var membership = {
|
var membership = new Membership({
|
||||||
_id: uuidv4(),
|
user: req.user,
|
||||||
user_id: req.user._id,
|
space: createdSpace,
|
||||||
space_id: attrs._id,
|
role: "admin"
|
||||||
role: "admin",
|
|
||||||
state: "active"
|
|
||||||
};
|
|
||||||
|
|
||||||
db.Membership.create(membership).then(() => {
|
|
||||||
res.status(201).json(createdSpace);
|
|
||||||
});
|
});
|
||||||
|
membership.save(function(err, createdTeam) {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
|
res.status(201).json(createdSpace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.parent_space_id) {
|
if (attrs.parent_space_id) {
|
||||||
db.Space.findOne({ where: {
|
Space.findOne({
|
||||||
"_id": attrs.parent_space_id
|
"_id": attrs.parent_space_id
|
||||||
}}).then(parentSpace => {
|
}).populate('creator', userMapping).exec((err, parentSpace) => {
|
||||||
if (parentSpace) {
|
if (parentSpace) {
|
||||||
db.getUserRoleInSpace(parentSpace, req.user, (role) => {
|
Space.roleInSpace(parentSpace, req.user, (err, role) => {
|
||||||
if ((role == "editor") || (role == "admin")) {
|
if ((role == "editor") || (role == "admin")) {
|
||||||
createSpace();
|
createSpace();
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
"error": "not editor in parent Space. role: "+role
|
"error": "not editor in parent Space"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -223,30 +292,6 @@ router.get('/:id', function(req, res, next) {
|
|||||||
res.status(200).json(req.space);
|
res.status(200).json(req.space);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/:id/path', (req, res) => {
|
|
||||||
// build up a breadcrumb trail (path)
|
|
||||||
var path = [];
|
|
||||||
var buildPath = (space) => {
|
|
||||||
if (space.parent_space_id) {
|
|
||||||
db.Space.findOne({ where: {
|
|
||||||
"_id": space.parent_space_id
|
|
||||||
}}).then(parentSpace => {
|
|
||||||
if (space._id == parentSpace._id) {
|
|
||||||
console.error("error: circular parent reference for space " + space._id);
|
|
||||||
res.send("error: circular reference");
|
|
||||||
} else {
|
|
||||||
path.push(parentSpace);
|
|
||||||
buildPath(parentSpace);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// reached the top
|
|
||||||
res.json(path.reverse());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildPath(req.space);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.put('/:id', function(req, res) {
|
router.put('/:id', function(req, res) {
|
||||||
var space = req.space;
|
var space = req.space;
|
||||||
var newAttr = req.body;
|
var newAttr = req.body;
|
||||||
@ -260,29 +305,27 @@ router.put('/:id', function(req, res) {
|
|||||||
newAttr.edit_slug = slug(newAttr['name']);
|
newAttr.edit_slug = slug(newAttr['name']);
|
||||||
|
|
||||||
delete newAttr['_id'];
|
delete newAttr['_id'];
|
||||||
|
delete newAttr['editor_name'];
|
||||||
delete newAttr['creator'];
|
delete newAttr['creator'];
|
||||||
delete newAttr['creator_id'];
|
|
||||||
delete newAttr['space_type'];
|
|
||||||
|
|
||||||
if (req['spaceRole'] != "admin") {
|
Space.findOneAndUpdate({
|
||||||
delete newAttr['access_mode']
|
|
||||||
delete newAttr['password']
|
|
||||||
delete newAttr['edit_hash']
|
|
||||||
delete newAttr['edit_slug']
|
|
||||||
delete newAttr['editors_locking']
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Space.update(newAttr, {where: {
|
|
||||||
"_id": space._id
|
"_id": space._id
|
||||||
}}).then(space => {
|
}, {
|
||||||
|
"$set": newAttr
|
||||||
|
}, {
|
||||||
|
"new": true
|
||||||
|
}, function(err, space) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
res.distributeUpdate("Space", space);
|
res.distributeUpdate("Space", space);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/:id/background', function(req, res, next) {
|
router.post('/:id/background', function(req, res, next) {
|
||||||
var space = req.space;
|
var space = req.space;
|
||||||
var newDate = new Date();
|
var newDate = new Date();
|
||||||
var fileName = (req.query.filename || "upload.jpg").replace(/[^a-zA-Z0-9\.]/g, '');
|
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
|
||||||
var localFilePath = "/tmp/" + fileName;
|
var localFilePath = "/tmp/" + fileName;
|
||||||
var writeStream = fs.createWriteStream(localFilePath);
|
var writeStream = fs.createWriteStream(localFilePath);
|
||||||
var stream = req.pipe(writeStream);
|
var stream = req.pipe(writeStream);
|
||||||
@ -291,18 +334,29 @@ router.post('/:id/background', function(req, res, next) {
|
|||||||
uploader.uploadFile("s" + req.space._id + "/bg_" + newDate.getTime() + "_" + fileName, "image/jpeg", localFilePath, function(err, backgroundUrl) {
|
uploader.uploadFile("s" + req.space._id + "/bg_" + newDate.getTime() + "_" + fileName, "image/jpeg", localFilePath, function(err, backgroundUrl) {
|
||||||
if (err) res.status(400).json(err);
|
if (err) res.status(400).json(err);
|
||||||
else {
|
else {
|
||||||
if (space.background_uri) {
|
var adv = space.advanced;
|
||||||
var oldPath = url.parse(req.space.background_uri).pathname;
|
|
||||||
|
if (adv.background_uri) {
|
||||||
|
var oldPath = url.parse(req.space.thumbnail_url).pathname;
|
||||||
uploader.removeFile(oldPath, function(err) {
|
uploader.removeFile(oldPath, function(err) {
|
||||||
console.error("removed old bg error:", err);
|
console.log("removed old bg error:", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Space.update({
|
adv.background_uri = backgroundUrl;
|
||||||
background_uri: backgroundUrl
|
|
||||||
|
Space.findOneAndUpdate({
|
||||||
|
"_id": space._id
|
||||||
}, {
|
}, {
|
||||||
where: { "_id": space._id }
|
"$set": {
|
||||||
}, function(rows) {
|
advanced: adv
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"new": true
|
||||||
|
}, function(err, space) {
|
||||||
|
if (err) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
} else {
|
||||||
fs.unlink(localFilePath, function(err) {
|
fs.unlink(localFilePath, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -311,24 +365,65 @@ router.post('/:id/background', function(req, res, next) {
|
|||||||
res.status(200).json(space);
|
res.status(200).json(space);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var handleDuplicateSpaceRequest = function(req, res, parentSpace) {
|
||||||
|
Space.duplicateSpace(req.space, req.user, 0, (err, newSpace) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
|
res.status(201).json(newSpace);
|
||||||
|
}
|
||||||
|
}, parentSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post('/:id/duplicate', (req, res, next) => {
|
||||||
|
if (req.query.parent_space_id) {
|
||||||
|
Space.findOne({
|
||||||
|
_id: req.query.parent_space_id
|
||||||
|
}).populate('creator', userMapping).exec((err, parentSpace) => {
|
||||||
|
if (!parentSpace) {
|
||||||
|
res.status(404).json({
|
||||||
|
"error": "parent space not found for dupicate"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Space.roleInSpace(parentSpace, req.user, (err, role) => {
|
||||||
|
if (role == "admin" || role == "editor") {
|
||||||
|
handleDuplicateSpaceRequest(req, res, parentSpace);
|
||||||
|
} else {
|
||||||
|
res.status(403).json({
|
||||||
|
"error": "not authed for parent_space_id"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleDuplicateSpaceRequest(req, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.delete('/:id', function(req, res, next) {
|
router.delete('/:id', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
const space = req.space;
|
const space = req.space;
|
||||||
|
|
||||||
if (req.spaceRole == "admin") {
|
if (req.spaceRole == "admin") {
|
||||||
const attrs = req.body;
|
const attrs = req.body;
|
||||||
space.destroy().then(function() {
|
Space.recursiveDelete(space, function(err) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
res.distributeDelete("Space", space);
|
res.distributeDelete("Space", space);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
"error": "requires admin role"
|
"error": "requires admin status"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -336,4 +431,139 @@ router.delete('/:id', function(req, res, next) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/:id/artifacts-pdf', function(req, res, next) {
|
||||||
|
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
|
||||||
|
|
||||||
|
var withZones = (req.query.zones) ? req.query.zones == "true" : false;
|
||||||
|
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
|
||||||
|
var localFilePath = "/tmp/" + fileName;
|
||||||
|
var writeStream = fs.createWriteStream(localFilePath);
|
||||||
|
var stream = req.pipe(writeStream);
|
||||||
|
|
||||||
|
req.on('end', function() {
|
||||||
|
|
||||||
|
var rawName = fileName.slice(0, fileName.length - 4);
|
||||||
|
var outputFolder = "/tmp/" + rawName;
|
||||||
|
var rights = 777;
|
||||||
|
|
||||||
|
fs.mkdir(outputFolder, function(db) {
|
||||||
|
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
|
||||||
|
|
||||||
|
exec.execFile("gs", ["-sDEVICE=jpeg", "-dDownScaleFactor=4", "-dDOINTERPOLATE", "-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-sOutputFile=" + images, "-r250", "-f", localFilePath], {}, function(error, stdout, stderr) {
|
||||||
|
if (error === null) {
|
||||||
|
|
||||||
|
glob(outputFolder + "/*.jpeg", function(er, files) {
|
||||||
|
var count = files.length;
|
||||||
|
var delta = 10;
|
||||||
|
|
||||||
|
var limitPerRow = Math.ceil(Math.sqrt(count));
|
||||||
|
|
||||||
|
var startX = parseInt(req.query.x, delta);
|
||||||
|
var startY = parseInt(req.query.y, delta);
|
||||||
|
|
||||||
|
async.mapLimit(files, 20, function(localfilePath, cb) {
|
||||||
|
|
||||||
|
var fileName = path.basename(localfilePath);
|
||||||
|
var baseName = path.basename(localfilePath, ".jpeg");
|
||||||
|
|
||||||
|
var number = parseInt(baseName.slice(baseName.length - 3, baseName.length), 10);
|
||||||
|
|
||||||
|
gm(localFilePath).size(function(err, size) {
|
||||||
|
var w = 350;
|
||||||
|
var h = w;
|
||||||
|
|
||||||
|
var x = startX + (((number - 1) % limitPerRow) * w);
|
||||||
|
var y = startY + ((parseInt(((number - 1) / limitPerRow), 10) + 1) * w);
|
||||||
|
|
||||||
|
var userId;
|
||||||
|
if (req.user)
|
||||||
|
userId = req.user._id;
|
||||||
|
|
||||||
|
var a = new Artifact({
|
||||||
|
mime: "image/jpg",
|
||||||
|
space_id: req.space._id,
|
||||||
|
user_id: userId,
|
||||||
|
editor_name: req.guest_name,
|
||||||
|
board: {
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
z: (number + (count + 100))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
|
||||||
|
if (error) res.status(400).json(error);
|
||||||
|
else {
|
||||||
|
if (withZones) {
|
||||||
|
var zone = new Artifact({
|
||||||
|
mime: "x-spacedeck/zone",
|
||||||
|
description: "Zone " + (number),
|
||||||
|
space_id: req.space._id,
|
||||||
|
user_id: userId,
|
||||||
|
editor_name: req.guest_name,
|
||||||
|
board: {
|
||||||
|
w: artifact.board.w + 20,
|
||||||
|
h: artifact.board.h + 40,
|
||||||
|
x: x - 10,
|
||||||
|
y: y - 30,
|
||||||
|
z: number
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
order: number,
|
||||||
|
valign: "middle",
|
||||||
|
align: "center"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
zone.save((err) => {
|
||||||
|
redis.sendMessage("create", "Artifact", zone.toJSON(), req.channelId);
|
||||||
|
cb(null, [artifact, zone]);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cb(null, [artifact]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}, function(err, artifacts) {
|
||||||
|
|
||||||
|
exec.execFile("rm", ["-r", outputFolder], function(err) {
|
||||||
|
res.status(201).json(_.flatten(artifacts));
|
||||||
|
|
||||||
|
async.eachLimit(artifacts, 10, (artifact_or_artifacts, cb) => {
|
||||||
|
|
||||||
|
if (artifact_or_artifacts instanceof Array) {
|
||||||
|
_.each(artifact_or_artifacts, (a) => {
|
||||||
|
redis.sendMessage("create", "Artifact", a.toJSON(), req.channelId);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
redis.sendMessage("create", "Artifact", artifact_or_artifacts.toJSON(), req.channelId);
|
||||||
|
}
|
||||||
|
cb(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error("error:", error);
|
||||||
|
exec.execFile("rm", ["-r", outputFolder], function(err) {
|
||||||
|
fs.unlink(localFilePath);
|
||||||
|
res.status(400).json({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(401).json({
|
||||||
|
"error": "no access"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
265
routes/api/teams.js
Normal file
265
routes/api/teams.js
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
var config = require('config');
|
||||||
|
require('../../models/schema');
|
||||||
|
|
||||||
|
var redis = require('../../helpers/redis');
|
||||||
|
var mailer = require('../../helpers/mailer');
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
var userMapping = { '_id': 1, 'nickname': 1, 'email': 1};
|
||||||
|
|
||||||
|
router.get('/:id', (req, res) => {
|
||||||
|
res.status(200).json(req.user.team);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/:id', (req, res) => {
|
||||||
|
var team = req.user.team;
|
||||||
|
if (!team) {
|
||||||
|
res.status(400).json({"error": "user in no team"});
|
||||||
|
} else {
|
||||||
|
var newAttr = req.body;
|
||||||
|
newAttr.updated_at = new Date();
|
||||||
|
delete newAttr['_id'];
|
||||||
|
|
||||||
|
if(newAttr['subdomain']) {
|
||||||
|
newAttr['subdomain'] = newAttr['subdomain'].toLowerCase();
|
||||||
|
}
|
||||||
|
const new_subdomain = newAttr['subdomain'];
|
||||||
|
var forbidden_subdomains = [];
|
||||||
|
|
||||||
|
function updateTeam() {
|
||||||
|
Team.findOneAndUpdate({"_id": team._id}, {"$set": newAttr}, {"new": true}, (err, team) => {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
res.status(200).json(team);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var isForbidden = forbidden_subdomains.indexOf(new_subdomain) > -1;
|
||||||
|
if (isForbidden) {
|
||||||
|
res.bad_request("subdomain not valid");
|
||||||
|
} else {
|
||||||
|
if (new_subdomain) {
|
||||||
|
Team.findOne({"domain": new_subdomain}).exec((err, team) => {
|
||||||
|
if(team) {
|
||||||
|
res.bad_request("subdomain already used");
|
||||||
|
} else {
|
||||||
|
updateTeam()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateTeam()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/:id/memberships', (req, res) => {
|
||||||
|
User
|
||||||
|
.find({team: req.user.team})
|
||||||
|
.populate("team")
|
||||||
|
.exec(function(err, users){
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
res.status(200).json(users);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/:id/memberships', (req, res, next) => {
|
||||||
|
if (req.body.email) {
|
||||||
|
const email = req.body.email.toLowerCase();
|
||||||
|
const team = req.user.team;
|
||||||
|
|
||||||
|
User.findOne({"email": email}).populate('team').exec((err, user) => {
|
||||||
|
if (user) {
|
||||||
|
const code = crypto.randomBytes(64).toString('hex').substring(0,7);
|
||||||
|
team.invitation_codes.push(code);
|
||||||
|
team.save((err) => {
|
||||||
|
if (err){ res.status(400).json(err); }
|
||||||
|
else {
|
||||||
|
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_membership_body", team.name), { action: {
|
||||||
|
link: config.endpoint + "/teams/" + req.user.team._id + "/join?code=" + code,
|
||||||
|
name: req.i18n.__("team_invite_membership_action"),
|
||||||
|
teamname: team.name
|
||||||
|
}});
|
||||||
|
|
||||||
|
res.status(201).json(user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// complete new user
|
||||||
|
const password = crypto.randomBytes(64).toString('hex').substring(0,7);
|
||||||
|
const confirmation_token = crypto.randomBytes(64).toString('hex').substring(0,7);
|
||||||
|
|
||||||
|
bcrypt.genSalt(10, (err, salt) => {
|
||||||
|
bcrypt.hash(password, salt, (err, hash) => {
|
||||||
|
crypto.randomBytes(16, (ex, buf) => {
|
||||||
|
const token = buf.toString('hex');
|
||||||
|
|
||||||
|
var u = new User({
|
||||||
|
email: email,
|
||||||
|
account_type: "email",
|
||||||
|
nickname: email,
|
||||||
|
team: team._id,
|
||||||
|
password_hash: hash,
|
||||||
|
payment_plan_key: team.payment_plan_key,
|
||||||
|
confirmation_token: confirmation_token,
|
||||||
|
preferences: {
|
||||||
|
language: req.i18n.locale
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
u.save((err) => {
|
||||||
|
if(err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
var homeSpace = new Space({
|
||||||
|
name: req.i18n.__("home"),
|
||||||
|
space_type: "folder",
|
||||||
|
creator: u
|
||||||
|
});
|
||||||
|
|
||||||
|
homeSpace.save((err, homeSpace) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
u.home_folder_id = homeSpace._id;
|
||||||
|
u.save((err) => {
|
||||||
|
|
||||||
|
User.find({"_id": {"$in": team.admins }}).exec((err, admins) => {
|
||||||
|
admins.forEach((admin) => {
|
||||||
|
var i18n = req.i18n;
|
||||||
|
if(admin.preferences && admin.preferences.language){
|
||||||
|
i18n.setLocale(admin.preferences.language || "en");
|
||||||
|
}
|
||||||
|
mailer.sendMail(admin.email, i18n.__("team_invite_membership_subject", team.name), i18n.__("team_invite_admin_body", email, team.name, password), { teamname: team.name });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_user_body", team.name, password), { action: {
|
||||||
|
link: config.endpoint + "/users/byteam/" + req.user.team._id + "/join?confirmation_token=" + confirmation_token,
|
||||||
|
name: req.i18n.__("team_invite_membership_action")
|
||||||
|
}, teamname: team.name });
|
||||||
|
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else{
|
||||||
|
res.status(201).json(u)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error": "email missing"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/:id/memberships/:user_id', (req, res) => {
|
||||||
|
User.findOne({_id: req.params.user_id}, (err,mem) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
if(user.team._id == req.user.team._id){
|
||||||
|
user['team'] = req.user.team._id;
|
||||||
|
user.save((err) => {
|
||||||
|
res.sendStatus(204);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.sendStatus(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/:id/memberships/:user_id/promote', (req, res) => {
|
||||||
|
User.findOne({_id: req.params.user_id}, (err,user) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
if (user.team.toString() == req.user.team._id.toString()) {
|
||||||
|
var team = req.user.team;
|
||||||
|
var adminIndex = team.admins.indexOf(user._id);
|
||||||
|
|
||||||
|
if (adminIndex == -1) {
|
||||||
|
team.admins.push(user._id);
|
||||||
|
team.save((err, team) => {
|
||||||
|
res.status(204).json(team);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error": "already admin"});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(403).json({"error": "team id not correct"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/:id/memberships/:user_id/demote', (req, res, next) => {
|
||||||
|
User.findOne({_id: req.params.user_id}, (err,user) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
if (user.team.toString() == req.user.team._id.toString()) {
|
||||||
|
const team = req.user.team;
|
||||||
|
const adminIndex = team.admins.indexOf(user._id);
|
||||||
|
|
||||||
|
if(adminIndex > -1) {
|
||||||
|
team.admins.splice(adminIndex,1);
|
||||||
|
team.save((err, team) => {
|
||||||
|
res.status(204).json(team);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.sendStatus(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/:id/memberships/:user_id', (req, res) => {
|
||||||
|
User.findOne({_id: req.params.user_id}).populate('team').exec((err,user) => {
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
const currentUserId = req.user._id.toString();
|
||||||
|
const team = req.user.team;
|
||||||
|
|
||||||
|
const isAdmin = (req.user.team.admins.filter( mem => {
|
||||||
|
return mem == currentUserId;
|
||||||
|
}).length == 1)
|
||||||
|
|
||||||
|
if (isAdmin) {
|
||||||
|
user.team = null;
|
||||||
|
user.payment_plan_key = "free";
|
||||||
|
user.save( err => {
|
||||||
|
const adminIndex = team.admins.indexOf(user._id);
|
||||||
|
if(adminIndex > -1) {
|
||||||
|
team.admins.splice(adminIndex,1);
|
||||||
|
team.save((err, team) => {
|
||||||
|
console.log("admin removed");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(403).json({"error": "not admin"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -1,16 +1,13 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
const db = require('../../models/db');
|
require('../../models/schema');
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
const os = require('os');
|
|
||||||
|
|
||||||
var mailer = require('../../helpers/mailer');
|
var mailer = require('../../helpers/mailer');
|
||||||
var uploader = require('../../helpers/uploader');
|
var uploader = require('../../helpers/uploader');
|
||||||
var importer = require('../../helpers/importer');
|
|
||||||
|
|
||||||
var bcrypt = require('bcryptjs');
|
var bcrypt = require('bcrypt');
|
||||||
var crypto = require('crypto');
|
var crypo = require('crypto');
|
||||||
var swig = require('swig');
|
var swig = require('swig');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
@ -18,125 +15,245 @@ var fs = require('fs');
|
|||||||
var request = require('request');
|
var request = require('request');
|
||||||
var gm = require('gm');
|
var gm = require('gm');
|
||||||
var validator = require('validator');
|
var validator = require('validator');
|
||||||
var URL = require('url').URL;
|
|
||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
var glob = require('glob');
|
|
||||||
|
|
||||||
router.get('/current', function(req, res, next) {
|
router.get('/current', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
var u = _.clone(req.user.dataValues);
|
console.log(req.user.team);
|
||||||
delete u.password_hash;
|
res.status(200).json(req.user);
|
||||||
delete u.password_reset_token;
|
|
||||||
delete u.confirmation_token;
|
|
||||||
u.token = req.cookies['sdsession'];
|
|
||||||
|
|
||||||
console.log(u);
|
|
||||||
|
|
||||||
res.status(200).json(u);
|
|
||||||
} else {
|
} else {
|
||||||
res.status(401).json({"error":"user_not_found"});
|
res.status(401).json({"error":"user_not_found"});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create user
|
|
||||||
router.post('/', function(req, res) {
|
router.post('/', function(req, res) {
|
||||||
if (!req.body["email"] || !req.body["password"]) {
|
if (req.body["email"] && req.body["password"]) {
|
||||||
res.status(400).json({"error":"email or password missing"});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var email = req.body["email"].toLowerCase();
|
var email = req.body["email"].toLowerCase();
|
||||||
var nickname = req.body["nickname"];
|
var nickname = req.body["nickname"];
|
||||||
var password = req.body["password"];
|
var password = req.body["password"];
|
||||||
var password_confirmation = req.body["password_confirmation"];
|
var password_confirmation = req.body["password_confirmation"];
|
||||||
var invite_code = req.body["invite_code"];
|
|
||||||
|
|
||||||
if (password_confirmation != password) {
|
if (password_confirmation == password) {
|
||||||
res.status(400).json({"error":"password_confirmation"});
|
if (validator.isEmail(email)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.invite_code && invite_code != config.invite_code) {
|
|
||||||
res.status(400).json({"error":"Invalid Invite Code."});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validator.isEmail(email)) {
|
|
||||||
res.status(400).json({"error":"email_invalid"});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var createUser = function() {
|
var createUser = function() {
|
||||||
bcrypt.genSalt(10, function(err, salt) {
|
bcrypt.genSalt(10, function(err, salt) {
|
||||||
bcrypt.hash(password, salt, function(err, hash) {
|
bcrypt.hash(password, salt, function(err, hash) {
|
||||||
crypto.randomBytes(16, function(ex, buf) {
|
|
||||||
|
crypo.randomBytes(16, function(ex, buf) {
|
||||||
var token = buf.toString('hex');
|
var token = buf.toString('hex');
|
||||||
|
|
||||||
var u = {
|
var u = new User({
|
||||||
_id: uuidv4(),
|
|
||||||
email: email,
|
email: email,
|
||||||
account_type: "email",
|
account_type: "email",
|
||||||
nickname: nickname,
|
nickname: nickname,
|
||||||
password_hash: hash,
|
password_hash: hash,
|
||||||
prefs_language: req.i18n.locale,
|
preferences: {
|
||||||
|
language: req.i18n.locale
|
||||||
|
},
|
||||||
confirmation_token: token
|
confirmation_token: token
|
||||||
};
|
});
|
||||||
|
|
||||||
db.User.create(u)
|
u.save(function (err) {
|
||||||
.error(err => {
|
if (err) res.sendStatus(400);
|
||||||
res.sendStatus(400);
|
else {
|
||||||
})
|
var homeSpace = new Space({
|
||||||
.then(u => {
|
|
||||||
var homeFolder = {
|
|
||||||
_id: uuidv4(),
|
|
||||||
name: req.i18n.__("home"),
|
name: req.i18n.__("home"),
|
||||||
space_type: "folder",
|
space_type: "folder",
|
||||||
creator_id: u._id
|
creator: u
|
||||||
};
|
});
|
||||||
db.Space.create(homeFolder)
|
|
||||||
.error(err => {
|
homeSpace.save((err, homeSpace) => {
|
||||||
res.sendStatus(400);
|
if (err) res.sendStatus(400);
|
||||||
})
|
else {
|
||||||
.then(homeFolder => {
|
u.home_folder_id = homeSpace._id;
|
||||||
u.home_folder_id = homeFolder._id;
|
u.save((err) => {
|
||||||
u.save()
|
|
||||||
.then(() => {
|
mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), {
|
||||||
// home folder created,
|
action: {
|
||||||
// auto accept pending invites
|
link: config.endpoint + "/confirm/" + u.confirmation_token,
|
||||||
db.Membership.update({
|
name: req.i18n.__("confirm_action")
|
||||||
"state": "active"
|
|
||||||
}, {
|
|
||||||
where: {
|
|
||||||
"email_invited": u.email,
|
|
||||||
"state": "pending"
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
res.status(201).json({});
|
res.status(201).json({});
|
||||||
})
|
}
|
||||||
.error(err => {
|
|
||||||
res.status(400).json(err);
|
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
db.User.findAll({where: {email: email}})
|
User.find({email: email}, (function (err, users) {
|
||||||
.then(users => {
|
if (err) {
|
||||||
if (users.length == 0) {
|
res.status(400).json({"error":"password_confirmation"});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
var domain = email.slice(email.lastIndexOf('@')+1);
|
||||||
|
|
||||||
|
Domain.findOne({domain: domain}, function(err, domain) {
|
||||||
|
if(domain){
|
||||||
|
if(domain.edu) {
|
||||||
createUser();
|
createUser();
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error":"domain_blocked"});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
createUser();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({"error":"user_email_already_used"});
|
res.status(400).json({"error":"user_email_already_used"});
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error":"email_invalid"});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error":"password_confirmation"});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error":"email or password missing"});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/current', function(req, res, next) {
|
router.get('/oauth2callback/url', function(req, res) {
|
||||||
|
var google = require('googleapis');
|
||||||
|
var OAuth2 = google.auth.OAuth2;
|
||||||
|
|
||||||
|
var oauth2Client = new OAuth2(
|
||||||
|
config.google_access,
|
||||||
|
config.google_secret,
|
||||||
|
config.endpoint + "/login"
|
||||||
|
);
|
||||||
|
|
||||||
|
var url = oauth2Client.generateAuthUrl({
|
||||||
|
access_type: 'online',
|
||||||
|
scope: "email"
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({"url":url});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/loginorsignupviagoogle', function(req, res) {
|
||||||
|
var google = require('googleapis');
|
||||||
|
var OAuth2 = google.auth.OAuth2;
|
||||||
|
var plus = google.plus('v1');
|
||||||
|
|
||||||
|
var oauth2Client = new OAuth2(
|
||||||
|
config.google_access,
|
||||||
|
config.google_secret,
|
||||||
|
config.endpoint + "/login"
|
||||||
|
);
|
||||||
|
|
||||||
|
var loginUser = function(user, cb) {
|
||||||
|
crypo.randomBytes(48, function(ex, buf) {
|
||||||
|
var token = buf.toString('hex');
|
||||||
|
var session = {
|
||||||
|
token: token,
|
||||||
|
created_at: new Date()
|
||||||
|
};
|
||||||
|
if(!user.sessions)
|
||||||
|
user.sessions = [];
|
||||||
|
user.sessions.push(session);
|
||||||
|
user.save(function(err, user) {
|
||||||
|
cb(session);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var code = req.query.code;
|
||||||
|
oauth2Client.getToken(code, function(err, tokens) {
|
||||||
|
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
var apiUrl = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=" + tokens.access_token;
|
||||||
|
|
||||||
|
var finalizeLogin = function(session){
|
||||||
|
var secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
|
||||||
|
res.cookie('sdsession', session.token, { httpOnly: true, secure: secure});
|
||||||
|
res.status(201).json(session);
|
||||||
|
};
|
||||||
|
|
||||||
|
request.get(apiUrl, function(error, response, body) {
|
||||||
|
if (error) res.status(400).json(error);
|
||||||
|
else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
const email = data.email;
|
||||||
|
const name = data.name;
|
||||||
|
|
||||||
|
User.findOne({email: email}, function (err, user) {
|
||||||
|
if (user) {
|
||||||
|
// login new google user
|
||||||
|
if (user.account_type == "google") {
|
||||||
|
// just login
|
||||||
|
loginUser(user, (session) => {
|
||||||
|
finalizeLogin(session);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({"error":"user_email_already_used"});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const u = new User({
|
||||||
|
email: email,
|
||||||
|
account_type: "google",
|
||||||
|
nickname: name,
|
||||||
|
avatar_thumb_uri: body.picture,
|
||||||
|
preferences: {
|
||||||
|
language: req.i18n.locale
|
||||||
|
},
|
||||||
|
confirmed_at: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
u.save(function (err) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
var homeSpace = new Space({
|
||||||
|
name: req.i18n.__("home"),
|
||||||
|
space_type: "folder",
|
||||||
|
creator: u
|
||||||
|
});
|
||||||
|
|
||||||
|
homeSpace.save(function(err, homeSpace) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
u.home_folder_id = homeSpace._id;
|
||||||
|
u.save(function(err){
|
||||||
|
if (err) res.sendStatus(400);
|
||||||
|
else {
|
||||||
|
|
||||||
|
mailer.sendMail(u.email, req.i18n.__("welcome_subject"), req.i18n.__("welcome_body"), {});
|
||||||
|
loginUser(u, function(session) {
|
||||||
|
finalizeLogin(session);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/ ', function(req, res, next) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
|
console.log(req.user.team);
|
||||||
res.status(200).json(req.user);
|
res.status(200).json(req.user);
|
||||||
} else {
|
} else {
|
||||||
res.status(401).json({"error":"user_not_found"});
|
res.status(401).json({"error":"user_not_found"});
|
||||||
@ -144,15 +261,19 @@ router.get('/current', function(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.put('/:id', function(req, res, next) {
|
router.put('/:id', function(req, res, next) {
|
||||||
// TODO explicit whitelisting
|
|
||||||
var user = req.user;
|
var user = req.user;
|
||||||
|
console.log(req.params.id, user._id);
|
||||||
if (user._id == req.params.id) {
|
if (user._id == req.params.id) {
|
||||||
var newAttr = req.body;
|
var newAttr = req.body;
|
||||||
newAttr.updated_at = new Date();
|
newAttr.updated_at = new Date();
|
||||||
delete newAttr['_id'];
|
delete newAttr['_id'];
|
||||||
|
|
||||||
db.User.update(newAttr, {where: {"_id": user._id}}).then(function(updatedUser) {
|
User.findOneAndUpdate({"_id": user._id}, {"$set": newAttr}, function(err, updatedUser) {
|
||||||
res.status(200).json(newAttr);
|
if (err) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
} else {
|
||||||
|
res.status(200).json(updatedUser);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
@ -170,41 +291,46 @@ router.post('/:id/password', function(req, res, next) {
|
|||||||
bcrypt.genSalt(10, function(err, salt) {
|
bcrypt.genSalt(10, function(err, salt) {
|
||||||
bcrypt.hash(pass, salt, function(err, hash) {
|
bcrypt.hash(pass, salt, function(err, hash) {
|
||||||
user.password_hash = hash;
|
user.password_hash = hash;
|
||||||
user.save().then(function() {
|
user.save(function(err){
|
||||||
|
if(err){
|
||||||
|
res.status(400).json(err);
|
||||||
|
}else{
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({"error": "Please enter the correct current password."});
|
res.status(403).json({"error": "old password wrong"});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({"error": "Access denied."});
|
res.status(403).json({"error": "wrong user"});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({"error": "Please choose a new password with at least 6 characters."});
|
res.status(400).json({"error": "password_to_short"});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/:id', (req, res, next) => {
|
router.delete('/:id', (req, res, next) => {
|
||||||
const user = req.user;
|
const user = req.user;
|
||||||
if(user._id == req.params.id) {
|
if(user._id == req.params.id) {
|
||||||
|
if (user.account_type == 'email') {
|
||||||
if (bcrypt.compareSync(req.query.password, user.password_hash)) {
|
if (bcrypt.compareSync(req.query.password, user.password_hash)) {
|
||||||
|
user.remove((err) => {
|
||||||
// TODO: this doesn't currently work.
|
|
||||||
// all objects (indirectly) belonging to the user have
|
|
||||||
// to be walked and deleted first.
|
|
||||||
|
|
||||||
user.destroy().then(err => {
|
|
||||||
if(err)res.status(400).json(err);
|
if(err)res.status(400).json(err);
|
||||||
else res.sendStatus(204);
|
else res.sendStatus(204);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.bad_request("Please enter the correct current password.");
|
res.bad_request("password_incorrect");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({error: "Access denied."});
|
user.remove((err) => {
|
||||||
|
if(err)res.status(400).json(err);
|
||||||
|
else res.sendStatus(204);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else res.status(403).json({error: ""});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/:user_id/confirm', (req, res) => {
|
router.put('/:user_id/confirm', (req, res) => {
|
||||||
@ -230,8 +356,8 @@ router.post('/:user_id/avatar', (req, res, next) => {
|
|||||||
const user = req.user;
|
const user = req.user;
|
||||||
const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg"
|
const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg"
|
||||||
|
|
||||||
const localFilePath = os.tmpdir()+"/"+filename;
|
const localFilePath = "/tmp/"+filename;
|
||||||
const localResizedFilePath = os.tmpdir()+"/resized_"+filename;
|
const localResizedFilePath = "/tmp/resized_"+filename;
|
||||||
const writeStream = fs.createWriteStream(localFilePath);
|
const writeStream = fs.createWriteStream(localFilePath);
|
||||||
const stream = req.pipe(writeStream);
|
const stream = req.pipe(writeStream);
|
||||||
|
|
||||||
@ -243,15 +369,19 @@ router.post('/:user_id/avatar', (req, res, next) => {
|
|||||||
if (err) res.status(400).json(err);
|
if (err) res.status(400).json(err);
|
||||||
else {
|
else {
|
||||||
user.avatar_thumb_uri = url;
|
user.avatar_thumb_uri = url;
|
||||||
user.save().then(() => {
|
user.save((err, updatedUser) => {
|
||||||
|
if (err) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
} else {
|
||||||
fs.unlink(localResizedFilePath, (err) => {
|
fs.unlink(localResizedFilePath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.status(400).json(err);
|
res.status(400).json(err);
|
||||||
} else {
|
} else {
|
||||||
res.status(200).json(user);
|
res.status(200).json(updatedUser);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -260,45 +390,72 @@ router.post('/:user_id/avatar', (req, res, next) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/feedback', function(req, res, next) {
|
||||||
|
var text = req.body.text;
|
||||||
|
// FIXME
|
||||||
|
mailer.sendMail("support@example.org", "Support Request by " + req.user.email, text, {reply_to: req.user.email});
|
||||||
|
res.sendStatus(201);
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/password_reset_requests', (req, res, next) => {
|
router.post('/password_reset_requests', (req, res, next) => {
|
||||||
const email = req.query.email;
|
const email = req.query.email;
|
||||||
db.User.findOne({where: {"email": email}}).then((user) => {
|
User.findOne({"email": email}).exec((err, user) => {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
if (user) {
|
if (user) {
|
||||||
crypto.randomBytes(16, (ex, buf) => {
|
if(user.account_type == "email") {
|
||||||
|
crypo.randomBytes(16, (ex, buf) => {
|
||||||
user.password_reset_token = buf.toString('hex');
|
user.password_reset_token = buf.toString('hex');
|
||||||
user.save().then(updatedUser => {
|
user.save((err, updatedUser) => {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
|
mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
|
||||||
link: config.endpoint + "/password-confirm/" + user.password_reset_token,
|
link: config.endpoint + "/password-confirm/" + user.password_reset_token,
|
||||||
name: req.i18n.__("password_reset_action")
|
name: req.i18n.__("password_reset_action")
|
||||||
}});
|
}});
|
||||||
res.status(201).json({});
|
res.status(201).json({});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json({"error": "error_unknown_email"});
|
res.status(404).json({"error": "error_unknown_email"});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
res.status(404).json({"error": "error_unknown_email"});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) {
|
router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) {
|
||||||
var password = req.body.password;
|
var password = req.body.password;
|
||||||
|
|
||||||
db.User
|
User
|
||||||
.findOne({where: {"password_reset_token": req.params.confirm_token}})
|
.findOne({"password_reset_token": req.params.confirm_token})
|
||||||
.then((user) => {
|
.exec((err, user) => {
|
||||||
|
if (err) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
} else {
|
||||||
if(user) {
|
if(user) {
|
||||||
bcrypt.genSalt(10, (err, salt) => {
|
bcrypt.genSalt(10, (err, salt) => {
|
||||||
bcrypt.hash(password, salt, function(err, hash) {
|
bcrypt.hash(password, salt, function(err, hash) {
|
||||||
|
|
||||||
user.password_hash = hash;
|
user.password_hash = hash;
|
||||||
user.password_token = null;
|
user.password_token = null;
|
||||||
user.save().then(function(updatedUser) {
|
user.save(function(err, updatedUser){
|
||||||
|
if (err) {
|
||||||
|
res.sendStatus(400);
|
||||||
|
} else {
|
||||||
res.sendStatus(201);
|
res.sendStatus(201);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('config');
|
var config = require('config');
|
||||||
require('../../models/db');
|
require('../../models/schema');
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var phantom = require('node-phantom-simple');
|
var phantom = require('node-phantom-simple');
|
||||||
|
171
routes/root.js
171
routes/root.js
@ -1,6 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
|
require('../models/schema');
|
||||||
|
|
||||||
const redis = require('../helpers/redis');
|
const redis = require('../helpers/redis');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@ -8,11 +9,7 @@ const crypto = require('crypto');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const mailer = require('../helpers/mailer');
|
const mailer = require('../helpers/mailer');
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
|
const qr = require('qr-image');
|
||||||
const db = require('../models/db');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const Op = Sequelize.Op;
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
res.render('index', { title: 'Spaces' });
|
res.render('index', { title: 'Spaces' });
|
||||||
@ -54,6 +51,10 @@ router.get('/password-confirm/:token', (req, res) => {
|
|||||||
res.render('spacedeck', { title: 'Signup' });
|
res.render('spacedeck', { title: 'Signup' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/team', (req, res) => {
|
||||||
|
res.render('spacedeck');
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/de/*', (req, res) => {
|
router.get('/de/*', (req, res) => {
|
||||||
res.redirect("/t/de");
|
res.redirect("/t/de");
|
||||||
});
|
});
|
||||||
@ -78,6 +79,10 @@ router.get('/en', (req, res) => {
|
|||||||
res.redirect("/t/end");
|
res.redirect("/t/end");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/it', (req, res) => {
|
||||||
|
res.redirect("/t/en");
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/account', (req, res) => {
|
router.get('/account', (req, res) => {
|
||||||
res.render('spacedeck');
|
res.render('spacedeck');
|
||||||
});
|
});
|
||||||
@ -90,6 +95,10 @@ router.get('/logout', (req, res) => {
|
|||||||
res.render('spacedeck');
|
res.render('spacedeck');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/users/oauth2callback', (req, res) => {
|
||||||
|
res.render('spacedeck');
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/contact', (req, res) => {
|
router.get('/contact', (req, res) => {
|
||||||
res.render('public/contact');
|
res.render('public/contact');
|
||||||
});
|
});
|
||||||
@ -116,12 +125,17 @@ router.get('/t/:id', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.get('/s/:token', (req, res) => {
|
router.get('/s/:token', (req, res) => {
|
||||||
|
redis.rateLimit(req.real_ip, "token", function(ok) {
|
||||||
|
if (ok) {
|
||||||
var token = req.params.token;
|
var token = req.params.token;
|
||||||
if (token.split("-").length > 0) {
|
if (token.split("-").length > 0) {
|
||||||
token = token.split("-")[0];
|
token = token.split("-")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Space.findOne({where: {"edit_hash": token}}).then(function (space) {
|
Space.findOne({"edit_hash": token}).exec(function (err, space) {
|
||||||
|
if (err) {
|
||||||
|
res.status(404).render('not_found', { title: 'Page Not Found.' });
|
||||||
|
} else {
|
||||||
if (space) {
|
if (space) {
|
||||||
if(req.accepts('text/html')){
|
if(req.accepts('text/html')){
|
||||||
res.redirect("/spaces/"+space._id + "?spaceAuth=" + token);
|
res.redirect("/spaces/"+space._id + "?spaceAuth=" + token);
|
||||||
@ -135,11 +149,156 @@ router.get('/s/:token', (req, res) => {
|
|||||||
res.status(404).json({});
|
res.status(404).json({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(429).json({"error": "Too Many Requests"});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/spaces/:id', (req, res) => {
|
router.get('/spaces/:id', (req, res) => {
|
||||||
|
if (req.headers['user-agent']) {
|
||||||
|
if (req.headers['user-agent'].match(/facebook/)) {
|
||||||
|
Space.findOne({"_id": req.params.id }).exec(function (err, space) {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json(err);
|
||||||
|
} else {
|
||||||
|
if (space) {
|
||||||
|
if (space.access_mode == "public") {
|
||||||
|
Artifact.find({"space_id": req.params.id }).populate("creator").exec(function(err, artifacts) {
|
||||||
|
space.artifacts = artifacts;
|
||||||
|
res.render('facebook', { space: space });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.redirect("/?error=space_not_accessible");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.render('not_found', { title: 'Spaces' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// not facebook, render javascript
|
||||||
res.render('spacedeck', { title: 'Space' });
|
res.render('spacedeck', { title: 'Space' });
|
||||||
|
}
|
||||||
|
} else res.render('spacedeck', { title: 'Space' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/users/byteam/:team_id/join', (req, res) => {
|
||||||
|
if (!req.user) {
|
||||||
|
const q = {confirmation_token: req.query.confirmation_token, account_type: "email", team: req.params.team_id};
|
||||||
|
User.findOne(q, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
res.status(400).json({"error":"session.users"});
|
||||||
|
} else {
|
||||||
|
if (user) {
|
||||||
|
crypto.randomBytes(48, function(ex, buf) {
|
||||||
|
const token = buf.toString('hex');
|
||||||
|
|
||||||
|
var session = {
|
||||||
|
token: token,
|
||||||
|
ip: req.ip,
|
||||||
|
device: "web",
|
||||||
|
created_at: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!user.sessions)
|
||||||
|
user.sessions = [];
|
||||||
|
|
||||||
|
user.sessions.push(session);
|
||||||
|
user.confirmed_at = new Date();
|
||||||
|
user.confirmation_token = null;
|
||||||
|
|
||||||
|
user.save(function(err, result) {
|
||||||
|
// FIXME
|
||||||
|
const secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
|
||||||
|
const domain = (process.env.NODE_ENV == "production") ? ".spacedeck.com" : ".spacedecklocal.de";
|
||||||
|
|
||||||
|
res.cookie('sdsession', token, { domain: domain, httpOnly: true, secure: secure});
|
||||||
|
res.redirect("/spaces");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(404).json({"error": "not found"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
res.redirect("/spaces");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/teams/:id/join', function(req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
if (!req.user.team) {
|
||||||
|
Team.findOne({"_id": req.params.id}, function(err, team) {
|
||||||
|
if (team) {
|
||||||
|
const idx = team.invitation_codes.indexOf(req.query.code);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const u = req.user;
|
||||||
|
u.team = team;
|
||||||
|
|
||||||
|
if(!u.confirmed_at) {
|
||||||
|
u.confirmed_at = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
u.payment_plan_key = team.payment_plan_key;
|
||||||
|
u.save(function(err) {
|
||||||
|
if (err) res.status(400).json(err);
|
||||||
|
else {
|
||||||
|
team.invitation_condes = team.invitation_codes.slice(idx);
|
||||||
|
team.save(function(err) {
|
||||||
|
team.invitation_codes = null;
|
||||||
|
|
||||||
|
var finish = function(team, users) {
|
||||||
|
User.find({"_id": {"$in": team.admins}}).exec((err, admins) => {
|
||||||
|
if(admins) {
|
||||||
|
admins.forEach((admin) => {
|
||||||
|
mailer.sendMail(
|
||||||
|
admin.email,
|
||||||
|
req.i18n.__("team_new_member_subject", team.name),
|
||||||
|
req.i18n.__("team_new_member_body", u.email, team.name)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
User.find({team: team}, function(err, users) {
|
||||||
|
finish(team, users);
|
||||||
|
res.redirect("/spaces");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.redirect("/spaces?error=team_code_notfound");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.redirect("/spaces?error=team_notfound");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.redirect("/spaces?error=team_already");
|
||||||
|
}
|
||||||
|
} else res.redirect("/login");
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/qrcode/:id', function(req, res) {
|
||||||
|
Space.findOne({"_id": req.params.id}).exec(function(err, space) {
|
||||||
|
if (space) {
|
||||||
|
const url = config.get("endpoint") + "/s/"+space.edit_hash;
|
||||||
|
const code = qr.image(url, { type: 'svg' });
|
||||||
|
res.type('svg');
|
||||||
|
code.pipe(res);
|
||||||
|
} else {
|
||||||
|
res.status(404).json({
|
||||||
|
"error": "not_found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
172
spacedeck.js
172
spacedeck.js
@ -1,172 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const db = require('./models/db.js');
|
|
||||||
require("log-timestamp");
|
|
||||||
|
|
||||||
const config = require('config');
|
|
||||||
const redis = require('./helpers/redis');
|
|
||||||
const websockets = require('./helpers/websockets');
|
|
||||||
|
|
||||||
const http = require('http');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const _ = require('underscore');
|
|
||||||
const favicon = require('serve-favicon');
|
|
||||||
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');
|
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
const app = express();
|
|
||||||
const serveStatic = require('serve-static');
|
|
||||||
|
|
||||||
const isProduction = app.get('env') === 'production';
|
|
||||||
|
|
||||||
// workaround for libssl_conf.so error triggered by phantomjs
|
|
||||||
process.env['OPENSSL_CONF'] = '/dev/null';
|
|
||||||
|
|
||||||
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
|
|
||||||
|
|
||||||
app.use(logger(isProduction ? 'combined' : 'dev'));
|
|
||||||
|
|
||||||
i18n.expressBind(app, {
|
|
||||||
locales: ["en", "de", "fr"],
|
|
||||||
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');
|
|
||||||
|
|
||||||
if (isProduction) {
|
|
||||||
app.set('views', path.join(__dirname, 'build', 'views'));
|
|
||||||
app.use(favicon(path.join(__dirname, 'build', 'assets', 'images', 'favicon.png')));
|
|
||||||
app.use(express.static(path.join(__dirname, 'build', 'assets')));
|
|
||||||
} else {
|
|
||||||
app.set('views', path.join(__dirname, 'views'));
|
|
||||||
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.png')));
|
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(bodyParser.json({
|
|
||||||
limit: '50mb'
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({
|
|
||||||
extended: false,
|
|
||||||
limit: '50mb'
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.use(cookieParser());
|
|
||||||
app.use(helmet.frameguard())
|
|
||||||
app.use(helmet.xssFilter())
|
|
||||||
app.use(helmet.hsts({
|
|
||||||
maxAge: 7776000000,
|
|
||||||
includeSubDomains: true
|
|
||||||
}))
|
|
||||||
app.disable('x-powered-by');
|
|
||||||
app.use(helmet.noSniff())
|
|
||||||
|
|
||||||
//app.use(require("./middlewares/error_helpers"));
|
|
||||||
app.use(require("./middlewares/session"));
|
|
||||||
//app.use(require("./middlewares/cors"));
|
|
||||||
app.use(require("./middlewares/i18n"));
|
|
||||||
app.use("/api", require("./middlewares/api_helpers"));
|
|
||||||
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
|
|
||||||
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
|
|
||||||
|
|
||||||
app.use('/api/users', require('./routes/api/users'));
|
|
||||||
app.use('/api/memberships', require('./routes/api/memberships'));
|
|
||||||
|
|
||||||
const spaceRouter = require('./routes/api/spaces');
|
|
||||||
app.use('/api/spaces', spaceRouter);
|
|
||||||
|
|
||||||
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
|
|
||||||
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
|
|
||||||
spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
|
|
||||||
spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
|
|
||||||
spaceRouter.use('/:id', require('./routes/api/space_exports'));
|
|
||||||
|
|
||||||
app.use('/api/sessions', require('./routes/api/sessions'));
|
|
||||||
//app.use('/api/webgrabber', require('./routes/api/webgrabber'));
|
|
||||||
app.use('/', require('./routes/root'));
|
|
||||||
|
|
||||||
if (config.get('storage_local_path')) {
|
|
||||||
app.use('/storage', serveStatic(config.get('storage_local_path')+"/"+config.get('storage_bucket'), {
|
|
||||||
maxAge: 24*3600
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
|
||||||
//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'));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = app;
|
|
||||||
|
|
||||||
// CONNECT TO DATABASE
|
|
||||||
db.init();
|
|
||||||
|
|
||||||
// START WEBSERVER
|
|
||||||
const port = 9666;
|
|
||||||
|
|
||||||
const server = http.Server(app).listen(port, () => {
|
|
||||||
|
|
||||||
if ("send" in process) {
|
|
||||||
process.send('online');
|
|
||||||
}
|
|
||||||
|
|
||||||
}).on('listening', () => {
|
|
||||||
|
|
||||||
const host = server.address().address;
|
|
||||||
const port = server.address().port;
|
|
||||||
console.log('Spacedeck Open listening at http://%s:%s', host, port);
|
|
||||||
|
|
||||||
}).on('error', (error) => {
|
|
||||||
|
|
||||||
if (error.syscall !== 'listen') {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
|
||||||
switch (error.code) {
|
|
||||||
case 'EACCES':
|
|
||||||
console.error(bind + ' requires elevated privileges');
|
|
||||||
process.exit(1);
|
|
||||||
break;
|
|
||||||
case 'EADDRINUSE':
|
|
||||||
console.error(bind + ' is already in use');
|
|
||||||
process.exit(1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
websockets.startWebsockets(server);
|
|
||||||
redis.connectRedis();
|
|
||||||
|
|
||||||
/*process.on('message', (message) => {
|
|
||||||
console.log("Process message:", message);
|
|
||||||
if (message === 'shutdown') {
|
|
||||||
console.log("Exiting Spacedeck.");
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
});*/
|
|
@ -26,12 +26,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.artifact-text.text-blank [contentEditable=true]:not(.text-editing) p:first-child::after {
|
/*&.artifact-text.text-blank [contentEditable=true]:not(.text-editing) p:first-child::after {
|
||||||
content: "Double click to edit";
|
content: "Double click to edit";
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*&.artifact-text.text-blank [contentEditable=true].text-editing p:first-child::after {
|
&.artifact-text.text-blank [contentEditable=true].text-editing p:first-child::after {
|
||||||
content: "Type here";
|
content: "Type here";
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}*/
|
}*/
|
||||||
@ -469,10 +469,11 @@
|
|||||||
color: black;
|
color: black;
|
||||||
//@include user-select(none);
|
//@include user-select(none);
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
font-size: 36px;
|
font-size: 18px;
|
||||||
|
|
||||||
&.artifact-zone {
|
&.artifact-zone {
|
||||||
background-color: rgba(0,0,0,0.05);
|
border: 1px solid rgba(46,204,113,1);
|
||||||
|
background-color: rgba(46,204,113,0.025);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
&:after {display: none; }
|
&:after {display: none; }
|
||||||
.shape {display: none; }
|
.shape {display: none; }
|
||||||
@ -552,10 +553,6 @@ body:not(.present-mode) {
|
|||||||
cursor: grab !important;
|
cursor: grab !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-note {
|
|
||||||
cursor: crosshair !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.artifact.state-idle {
|
.artifact.state-idle {
|
||||||
.progress, .progress-text {
|
.progress, .progress-text {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -7,6 +7,12 @@
|
|||||||
|
|
||||||
.btn-group.colors {
|
.btn-group.colors {
|
||||||
.btn {
|
.btn {
|
||||||
|
// padding: 4px;
|
||||||
|
// background-clip: content-box;
|
||||||
|
// padding-right: 2px;
|
||||||
|
// &:last-child {
|
||||||
|
// padding-right: 4px;
|
||||||
|
// }
|
||||||
box-shadow: inset 0 0 30px 0px rgba(40,40,40,0.1);
|
box-shadow: inset 0 0 30px 0px rgba(40,40,40,0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +64,7 @@
|
|||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
color: $black;
|
color: $medium;;
|
||||||
|
|
||||||
@include user-select(none);
|
@include user-select(none);
|
||||||
&:last-child {border: none;}
|
&:last-child {border: none;}
|
||||||
@ -76,9 +82,12 @@
|
|||||||
|
|
||||||
&.btn-link {
|
&.btn-link {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: $medium;
|
color: $medium;;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.facebook {background-color: $facebook !important; color: white !important;}
|
||||||
|
&.twitter {background-color: $twitter !important; color: white !important; }
|
||||||
|
|
||||||
&.btn-round {
|
&.btn-round {
|
||||||
border-radius: 100px !important;
|
border-radius: 100px !important;
|
||||||
}
|
}
|
||||||
@ -87,10 +96,21 @@
|
|||||||
border-radius: 6px !important;
|
border-radius: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// &.close {
|
||||||
|
// position: absolute;
|
||||||
|
// top: 15px;
|
||||||
|
// right: 15px;
|
||||||
|
// z-index: 4000;
|
||||||
|
// font-size: 40px;
|
||||||
|
// }
|
||||||
|
|
||||||
&.btn-nude {
|
&.btn-nude {
|
||||||
min-width: 0 !important;
|
min-width: 0 !important;
|
||||||
|
// font-size: inherit !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
// height: auto !important;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-nude + .btn-nude {
|
&.btn-nude + .btn-nude {
|
||||||
@ -103,7 +123,7 @@
|
|||||||
|
|
||||||
&.btn-stroke {
|
&.btn-stroke {
|
||||||
box-shadow: inset 0 0 0 1px $dark;
|
box-shadow: inset 0 0 0 1px $dark;
|
||||||
color: $black;
|
color: $dark !important;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
&:active {
|
&:active {
|
||||||
box-shadow: inset 0 0 0 1px white;
|
box-shadow: inset 0 0 0 1px white;
|
||||||
@ -112,8 +132,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.btn-stroke-darken {
|
&.btn-stroke-darken {
|
||||||
border: 1px solid $black;
|
//box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
|
||||||
color: $black;
|
border: 1px solid rgba(0,0,0,0.1);
|
||||||
|
color: $medium;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
&:active {
|
&:active {
|
||||||
//box-shadow: inset 0 0 0 1px $dark;
|
//box-shadow: inset 0 0 0 1px $dark;
|
||||||
@ -242,18 +263,9 @@
|
|||||||
|
|
||||||
&.btn-transparent {
|
&.btn-transparent {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: $dark;
|
color: $medium;
|
||||||
&.active {
|
&.active {color: $darker !important; }
|
||||||
//color: $black !important;
|
&.open {color: white !important; }
|
||||||
color: $white;
|
|
||||||
background-color: $black;
|
|
||||||
}
|
|
||||||
&.open {
|
|
||||||
//color: $black !important;
|
|
||||||
color: $white;
|
|
||||||
background-color: $black;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-transparent-medium {
|
&.btn-transparent-medium {
|
||||||
@ -301,7 +313,7 @@
|
|||||||
|
|
||||||
&.btn-dark {
|
&.btn-dark {
|
||||||
background-color: $dark ;
|
background-color: $dark ;
|
||||||
color: $white;
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-medium {
|
&.btn-medium {
|
||||||
@ -469,6 +481,7 @@
|
|||||||
|
|
||||||
&.btn-icon {
|
&.btn-icon {
|
||||||
padding: 0px !important;
|
padding: 0px !important;
|
||||||
|
font-weight: bold;
|
||||||
max-width: 60px;
|
max-width: 60px;
|
||||||
|
|
||||||
&.btn-xl { max-width: 80px; }
|
&.btn-xl { max-width: 80px; }
|
||||||
@ -495,6 +508,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn-social {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover .icon,
|
||||||
|
.number {
|
||||||
|
@include scale(0,0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .number {
|
||||||
|
@include transition( all 0.1s 0.1s ease-in-out);
|
||||||
|
@include scale(1,1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number,
|
||||||
|
.icon {
|
||||||
|
@include transition( all 0.1s 0s ease-in-out);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.btn-md.btn-icon-labeled {
|
&.btn-md.btn-icon-labeled {
|
||||||
.icon:before {
|
.icon:before {
|
||||||
line-height: 29px;
|
line-height: 29px;
|
||||||
@ -530,6 +567,7 @@
|
|||||||
.icon:before {line-height: 42px; }
|
.icon:before {line-height: 42px; }
|
||||||
.icon-label {
|
.icon-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
text-transform: capitalize;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
display: block;
|
display: block;
|
||||||
@ -542,7 +580,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding: 0 0px;
|
padding: 0 0px;
|
||||||
font-weight: 300;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hover {
|
&.hover {
|
||||||
@ -676,6 +714,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
border-radius: 0 !important;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
float: left;
|
float: left;
|
||||||
@ -736,7 +775,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.btn-group {
|
.btn-group {
|
||||||
//@include scale(0,0);
|
@include scale(0,0);
|
||||||
//@include transition( all 0.1s 0s ease-in-out);
|
//@include transition( all 0.1s 0s ease-in-out);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -748,7 +787,7 @@
|
|||||||
margin-left: -12px;
|
margin-left: -12px;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
//@include scale(0,0);
|
@include scale(0,0);
|
||||||
//@include transition( all 0.1s 0.05s ease-in-out);
|
//@include transition( all 0.1s 0.05s ease-in-out);
|
||||||
|
|
||||||
|
|
||||||
@ -940,7 +979,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// !btn-group
|
.btn-group.bottom-left > .btn {
|
||||||
|
border-radius: 0px;
|
||||||
|
|
||||||
|
&:first-child{
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
}
|
||||||
|
&.last,
|
||||||
|
&:last-child{
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-xyz {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
font-size: 0px;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
@include clearfix;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -951,16 +1014,13 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
//border: 1px solid $dark;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
&.dark {
|
&.dark {
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
background-color: $dark;
|
background-color: $dark;
|
||||||
color: $white;
|
color: $lighter;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
color: $white;
|
color: $lighter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,14 +96,15 @@
|
|||||||
border-bottom-right-radius: $radius*3;
|
border-bottom-right-radius: $radius*3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-account {
|
|
||||||
width: 600px;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
ol, ul, p {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
> .btn-block:last-child {
|
> .btn-block:last-child {
|
||||||
border-top-left-radius: 0px;
|
border-top-left-radius: 0px;
|
||||||
border-top-right-radius: 0px;
|
border-top-right-radius: 0px;
|
||||||
@ -111,21 +112,24 @@
|
|||||||
border-bottom-right-radius: $radius*3;
|
border-bottom-right-radius: $radius*3;
|
||||||
}
|
}
|
||||||
|
|
||||||
position: absolute;
|
min-width: 200px;
|
||||||
font-size: 15px;
|
|
||||||
border: 1px solid black;
|
@include backface-visibility(hidden);
|
||||||
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
|
|
||||||
border-radius: 5px;
|
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
z-index: 1000;
|
||||||
|
position: absolute;
|
||||||
|
// white-space: normal;
|
||||||
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@include user-select(none);
|
||||||
@include transition( all 0.125s ease-in-out);
|
@include transition( all 0.125s ease-in-out);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
color: $dark;
|
color: $medium;
|
||||||
&.dark {
|
&.dark {background-color: $dark; }
|
||||||
background-color: $dark;
|
|
||||||
}
|
border-radius: $radius*3;
|
||||||
|
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05), 0 2px 7px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.dialog-tabs-wrapper {
|
.dialog-tabs-wrapper {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -146,13 +150,15 @@
|
|||||||
&:hover span {color: $dark; }
|
&:hover span {color: $dark; }
|
||||||
|
|
||||||
&.open span {
|
&.open span {
|
||||||
background-color: white;
|
background-color: $light;
|
||||||
color: $dark;
|
color: $dark;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05), 0 2px 7px rgba(0, 0, 0, 0.1) !important;
|
||||||
border-bottom-right-radius: 0px !important;
|
border-bottom-right-radius: 0px !important;
|
||||||
border-bottom-left-radius: 0px !important;
|
border-bottom-left-radius: 0px !important;
|
||||||
border-top-left-radius: $radius*3;
|
border-top-left-radius: $radius*3;
|
||||||
border-top-right-radius: $radius*3;
|
border-top-right-radius: $radius*3;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child span {
|
&:first-child span {
|
||||||
@ -194,6 +200,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.dialog-section {
|
.dialog-section {
|
||||||
&:first-child {border: none !important; }
|
&:first-child {border: none !important; }
|
||||||
border-top: 2px solid rgba(0,0,0,0.1);
|
border-top: 2px solid rgba(0,0,0,0.1);
|
||||||
@ -221,13 +228,4 @@
|
|||||||
h4 .icon {
|
h4 .icon {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// account dialog
|
|
||||||
&.dialog-freestanding {
|
|
||||||
margin: auto;
|
|
||||||
position: relative;
|
|
||||||
top: 150px;
|
|
||||||
border: none;
|
|
||||||
width: 800px;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -43,6 +43,9 @@ $predelay: 0;
|
|||||||
|
|
||||||
&.hover:hover,
|
&.hover:hover,
|
||||||
&.open {
|
&.open {
|
||||||
|
// &:before {opacity: 0.125; }
|
||||||
|
// pointer-events: auto;
|
||||||
|
background-color: $dark;
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
@ -119,10 +122,6 @@ $predelay: 0;
|
|||||||
position: relative;
|
position: relative;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdown-block {
|
&.dropdown-block {
|
||||||
display: block;
|
display: block;
|
||||||
.dropdown-toggle {
|
.dropdown-toggle {
|
||||||
@ -144,7 +143,8 @@ $predelay: 0;
|
|||||||
|
|
||||||
&.light > .dropdown-menu,
|
&.light > .dropdown-menu,
|
||||||
&.light > .dialog {
|
&.light > .dialog {
|
||||||
background: white;
|
background: $light;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .dropdown-menu {
|
> .dropdown-menu {
|
||||||
@ -189,6 +189,8 @@ $predelay: 0;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
&.hover:hover > .dialog,
|
&.hover:hover > .dialog,
|
||||||
&.hover:hover > .dropdown-menu,
|
&.hover:hover > .dropdown-menu,
|
||||||
|
|
||||||
@ -204,7 +206,9 @@ $predelay: 0;
|
|||||||
&.open {
|
&.open {
|
||||||
> .dialog,
|
> .dialog,
|
||||||
> .dropdown-menu {
|
> .dropdown-menu {
|
||||||
//transform: translate3d(-50%, -50%, 100px) scale(1);
|
-webkit-transform: translate3d(-50%, -50%, 100px) scale(1);
|
||||||
|
-ms-transform: translate3d(-50%, -50%, 100px) scale(1);
|
||||||
|
transform: translate3d(-50%, -50%, 100px) scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,8 +217,10 @@ $predelay: 0;
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
//@include transform-origin(center center);
|
@include transform-origin(center center);
|
||||||
//transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
|
-webkit-transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
|
||||||
|
-ms-transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
|
||||||
|
transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,8 +230,10 @@ $predelay: 0;
|
|||||||
top: auto;
|
top: auto;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
//@include transform-origin(bottom left);
|
@include transform-origin(bottom left);
|
||||||
//transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
|
-webkit-transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
|
||||||
|
-ms-transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
|
||||||
|
transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +243,10 @@ $predelay: 0;
|
|||||||
top: auto;
|
top: auto;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
//@include transform-origin(bottom center);
|
@include transform-origin(bottom center);
|
||||||
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
-webkit-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
|
-ms-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
|
transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -247,16 +257,10 @@ $predelay: 0;
|
|||||||
top: 100%;
|
top: 100%;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
margin-top: -16px;
|
margin-top: -16px;
|
||||||
//@include transform-origin(top center);
|
@include transform-origin(top center);
|
||||||
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
-webkit-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
}
|
-ms-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
}
|
transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
|
||||||
|
|
||||||
&.top.left {
|
|
||||||
> .dialog,
|
|
||||||
> .dropdown-menu {
|
|
||||||
left: 70px;
|
|
||||||
margin-top: -60px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,18 +270,20 @@ $predelay: 0;
|
|||||||
top: 100%;
|
top: 100%;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
|
right: 0;
|
||||||
right: 70px;
|
margin-top: 16px;
|
||||||
margin-top: -60px;
|
@include transform-origin(top right);
|
||||||
|
-webkit-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
|
||||||
//@include transform-origin(top right);
|
-ms-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
|
||||||
//transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
|
transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
|
||||||
}
|
}
|
||||||
&.hover:hover,
|
&.hover:hover,
|
||||||
&.open {
|
&.open {
|
||||||
> .dialog,
|
> .dialog,
|
||||||
> .dropdown-menu {
|
> .dropdown-menu {
|
||||||
//transform: translate3d(0%, 0%, 100px) scale(1);
|
-webkit-transform: translate3d(0%, 0%, 100px) scale(1);
|
||||||
|
-ms-transform: translate3d(0%, 0%, 100px) scale(1);
|
||||||
|
transform: translate3d(0%, 0%, 100px) scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +312,9 @@ $predelay: 0;
|
|||||||
|
|
||||||
> .dialog,
|
> .dialog,
|
||||||
> .dropdown-menu {
|
> .dropdown-menu {
|
||||||
//transform: translate3d(-50%, 0%, 100px) scale(1);
|
-webkit-transform: translate3d(-50%, 0%, 100px) scale(1);
|
||||||
|
-ms-transform: translate3d(-50%, 0%, 100px) scale(1);
|
||||||
|
transform: translate3d(-50%, 0%, 100px) scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,7 +324,9 @@ $predelay: 0;
|
|||||||
&.open {
|
&.open {
|
||||||
> .dialog,
|
> .dialog,
|
||||||
> .dropdown-menu {
|
> .dropdown-menu {
|
||||||
//transform: translate3d(-33%, 0%, 100px) scale(1) !important;
|
-webkit-transform: translate3d(-33%, 0%, 100px) scale(1) !important;
|
||||||
|
-ms-transform: translate3d(-33%, 0%, 100px) scale(1) !important;
|
||||||
|
transform: translate3d(-33%, 0%, 100px) scale(1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,7 +334,7 @@ $predelay: 0;
|
|||||||
|
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
/*&.options-3 {
|
&.options-3 {
|
||||||
&.option-1:after { margin-left: -68px;}
|
&.option-1:after { margin-left: -68px;}
|
||||||
&.option-2:after { margin-left: -8px;}
|
&.option-2:after { margin-left: -8px;}
|
||||||
&.option-3:after { margin-left: 52px;}
|
&.option-3:after { margin-left: 52px;}
|
||||||
@ -338,9 +348,8 @@ $predelay: 0;
|
|||||||
-webkit-transform: scale(1);
|
-webkit-transform: scale(1);
|
||||||
-ms-transform: scale(1);
|
-ms-transform: scale(1);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
&:after {
|
&:after {
|
||||||
@include transition( all 0.1s ease-in-out 0s);
|
@include transition( all 0.1s ease-in-out 0s);
|
||||||
content: "";
|
content: "";
|
||||||
@ -353,24 +362,26 @@ $predelay: 0;
|
|||||||
margin-left: -8px;
|
margin-left: -8px;
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
//transform: scale(0,0);
|
-webkit-transform: scale(0,0);
|
||||||
|
-ms-transform: scale(0,0);
|
||||||
|
transform: scale(0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.bottom:after, &.bottomleft:after {
|
&.bottom:after, &.bottomleft:after {
|
||||||
//@include transform-origin(bottom center);
|
@include transform-origin(bottom center);
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
border-bottom: 8px solid transparent;
|
border-bottom: 8px solid transparent;
|
||||||
border-right: 8px solid transparent;
|
border-right: 8px solid transparent;
|
||||||
border-top: 8px solid #303030;
|
border-top: 8px solid #303030;
|
||||||
border-left: 8px solid transparent;
|
border-left: 8px solid transparent;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
/*&.top:after {
|
&.top:after {
|
||||||
@include transform-origin(top center);
|
@include transform-origin(top center);
|
||||||
top: 100%;
|
top: 100%;
|
||||||
border-bottom: 8px solid #303030;
|
border-bottom: 8px solid #303030;
|
||||||
border-right: 8px solid transparent;
|
border-right: 8px solid transparent;
|
||||||
border-top: 8px solid transparent;
|
border-top: 8px solid transparent;
|
||||||
border-left: 8px solid transparent;
|
border-left: 8px solid transparent;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
@ -254,6 +254,7 @@
|
|||||||
// word-wrap: break-word;
|
// word-wrap: break-word;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
|
box-shadow: 0 0 1pxrgba(0,0,0,0.1);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-right: $folder-gutter*2;
|
padding-right: $folder-gutter*2;
|
||||||
@ -396,10 +397,7 @@
|
|||||||
|
|
||||||
&:active { opacity: 0.95 !important; }
|
&:active { opacity: 0.95 !important; }
|
||||||
|
|
||||||
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.025), 0 2px 7px rgba(0, 0, 0, 0.025);
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
// ???
|
|
||||||
@include opacity(1);
|
@include opacity(1);
|
||||||
color: $medium;
|
color: $medium;
|
||||||
// color: white;
|
// color: white;
|
||||||
@ -478,6 +476,7 @@
|
|||||||
left: 0px;
|
left: 0px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
background-color: rgba(255,255,255,1);
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -502,6 +501,30 @@
|
|||||||
color: $dark;
|
color: $dark;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-social {
|
||||||
|
padding: 8px;
|
||||||
|
border-right: 2px solid rgba(0,0,0,0.025);
|
||||||
|
@include clearfix;
|
||||||
|
color: $medium;
|
||||||
|
|
||||||
|
.item-likes,
|
||||||
|
.item-comments,
|
||||||
|
.item-shares {
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
.icon {opacity: 0; }
|
||||||
|
.number {opacity: 1; }
|
||||||
|
}
|
||||||
|
.number {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.icon {opacity: 0.5; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-appendix {
|
.item-appendix {
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
color: $medium;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
|
@ -2,14 +2,24 @@
|
|||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
|
||||||
.input-select {
|
.input-select {
|
||||||
background-color: rgba(255,255,255,0.04);
|
// background-color: rgba(255,255,255,0.04);
|
||||||
background-image: url('images/select_arrow.gif');
|
// background-image: url('images/select_arrow.gif');
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
select.input{
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
-webkit-appearance:none;
|
||||||
|
// -moz-appearance:window;
|
||||||
appearance:none;
|
appearance:none;
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -23,6 +23,7 @@ input:invalid {
|
|||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
font-size: 10px;
|
||||||
margin: 12px;
|
margin: 12px;
|
||||||
color: red;
|
color: red;
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
@ -112,26 +113,43 @@ select {
|
|||||||
|
|
||||||
&.input-white {
|
&.input-white {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
color: $medium;
|
||||||
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.05), inset 0 0px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.05), inset 0 0px 4px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
&.input-light {
|
&.input-light {
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.input-dark {
|
&.input-dark {
|
||||||
background-color: $darker;
|
background-color: $darker;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.input-lighten {
|
&.input-lighten {
|
||||||
background-color: rgba(255,255,255,0.05);
|
background-color: rgba(255,255,255,0.05);
|
||||||
|
color: $medium !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.input-darken {
|
&.input-darken {
|
||||||
background-color: rgba(0,0,0,0.05);
|
background-color: rgba(0,0,0,0.05);
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.input-transparent {
|
&.input-transparent {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
color: $medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
// &:focus {color: white; }
|
||||||
|
|
||||||
|
&:invalid {
|
||||||
|
// background-color: rgba(198,101,84,0.05);
|
||||||
|
// color: rgba(198,101,84,0.75)
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include input-focus();
|
@include input-focus();
|
||||||
|
@ -69,27 +69,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.handles {
|
.handles {
|
||||||
//border: 1px solid rgba(255,255,255,0.5);
|
// background-color: rgba(40,140,215,0.45);
|
||||||
|
border: 1px solid rgba(255,255,255,0.5);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: -1;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 800;
|
z-index: 800;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
background: rgba(255,255,255,0.1);
|
|
||||||
|
|
||||||
&:after{
|
&:after{
|
||||||
border: 4px dotted #000000;
|
border: 1px dotted rgba(40,140,215,1);
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
top: 0px;
|
top: -1px;
|
||||||
left: 0px;
|
left: -1px;
|
||||||
right: 0px;
|
right: -1px;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +97,7 @@
|
|||||||
border: 8px solid rgba(255,255,255,0.5);
|
border: 8px solid rgba(255,255,255,0.5);
|
||||||
|
|
||||||
&:after{
|
&:after{
|
||||||
border: 8px dotted #000000;
|
border: 8px dotted rgba(40,140,215,1);
|
||||||
top: -4px;
|
top: -4px;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
right: -4px;
|
right: -4px;
|
||||||
@ -333,14 +332,15 @@
|
|||||||
pointer-events:auto;
|
pointer-events:auto;
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: 30px !important;
|
||||||
|
height: 30px !important;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
|
margin: -15px;
|
||||||
|
|
||||||
border: 1px solid black;
|
border: 1px solid rgba(0,0,0,0.25);
|
||||||
margin: -5px;
|
|
||||||
padding: 4px;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: black;
|
background-color: rgba(255,255,255,0.5);
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,7 +428,14 @@
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 10px;
|
border-width: 10px;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
-webkit-background-clip: padding-box;
|
||||||
|
-moz-background-clip: padding-box;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
|
|
||||||
|
-webkit-transition: all .05s ease-in-out;
|
||||||
|
-moz-transition: all .05s ease-in-out;
|
||||||
|
-ms-transition: all .05s ease-in-out;
|
||||||
|
-o-transition: all .05s ease-in-out;
|
||||||
transition: all .05s ease-in-out;
|
transition: all .05s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
.header-left,
|
.header-left,
|
||||||
.header-right {
|
.header-right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
//@include transition( all 0.25s ease-in-out);
|
||||||
@include backface-visibility(hidden);
|
@include backface-visibility(hidden);
|
||||||
z-index: 3000;
|
z-index: 3000;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
@ -26,21 +27,21 @@
|
|||||||
.home {
|
.home {
|
||||||
margin-top: -20px;
|
margin-top: -20px;
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
|
// .icon {color: $dark; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-left {
|
.header-left {
|
||||||
|
@include transform-origin(center left);
|
||||||
left: 0;
|
left: 0;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-left: 20px;
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
}
|
||||||
.header-right {
|
.header-right {
|
||||||
|
@include transform-origin(center right);
|
||||||
right: 0;
|
right: 0;
|
||||||
padding-right: 20px;
|
padding-right: 10px;
|
||||||
padding-top: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-center {
|
.header-center {
|
||||||
|
@include transform-origin(center center);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -55,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.header-left > * { margin-right: 10px; }
|
.header-left > * { margin-right: 10px; }
|
||||||
.header-right > * { margin-left: 10px; }
|
.header-right > * { margin-left: 5px; }
|
||||||
.header-right { font-size: 0;}
|
.header-right { font-size: 0;}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
@ -89,3 +90,21 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.present-mode #space-header {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#space-siblings {
|
||||||
|
background-color: rgba(245, 245, 245, 0.95);
|
||||||
|
padding: 35px;
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
margin-top: 54px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -85,12 +85,3 @@
|
|||||||
transform: rotateZ(45deg) translateX(-8px);
|
transform: rotateZ(45deg) translateX(-8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
background-size: 26px;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-sd6 {
|
|
||||||
background-image: url(/images/sd6-icon-white.svg);
|
|
||||||
}
|
|
||||||
|
@ -1,52 +1,257 @@
|
|||||||
@import "vars";
|
@import "vars";
|
||||||
|
|
||||||
#landing-header {
|
#landing-header {
|
||||||
background-color: white;
|
background-color: rgba(255,255,255,0.3);
|
||||||
height: 64px;
|
height: 64px;
|
||||||
position: relative;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#landing {
|
.landing-keyvisual-wrapper {
|
||||||
margin-top: 100px;
|
background-image: url("../images/sd5-keyvisual-compressed.jpg");
|
||||||
|
background-size: cover;
|
||||||
section {
|
background-position: center;
|
||||||
margin-left: 300px;
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
> * {
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.landing-plans-wrapper {
|
||||||
|
background-image: url("../images/sd5-hero2-compressed.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
padding-top: 80px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-box {
|
||||||
|
width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
max-width: 90%;
|
||||||
|
background-color: white;
|
||||||
|
padding: 40px;
|
||||||
|
margin-bottom: 80px;
|
||||||
|
margin-top: 80px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
box-shadow: 0px 0px 50px rgba(0,0,0,0.2);
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.black {
|
||||||
|
background-color: #222;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.overlap {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
margin-top: -65px;
|
||||||
|
left: 50%;
|
||||||
|
top: 0px;
|
||||||
|
margin-left: -250px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.screenshot {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 90%;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.landing-box-left {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-xxl {
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-xl {
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-box {
|
||||||
|
background: linear-gradient(to bottom, #FEFFFF 25%,#D0D8E2 100%);
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-box.plans-box {
|
||||||
|
margin-top: 200px;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-table {
|
||||||
|
tr {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-size: 42px;
|
||||||
|
padding-top: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.best-plan {
|
||||||
|
padding-top: 20px;
|
||||||
|
font-size: 48px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 20px;
|
||||||
|
width: 30%;
|
||||||
|
p, li {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td.best-plan {
|
||||||
|
width: 40%;
|
||||||
|
p {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td li {
|
||||||
|
list-style-type: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-buttons {
|
||||||
|
text-align:center;
|
||||||
|
margin-top:20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-row {
|
||||||
|
position: relative;
|
||||||
|
padding: 80px;
|
||||||
|
background-color: white;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
background-color: $blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-row div {
|
||||||
|
display: inline-block;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-row {
|
||||||
|
background-color: white;
|
||||||
|
padding-bottom: 80px;
|
||||||
|
padding-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyvisual {
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0px 0px 20px #eee;
|
||||||
|
|
||||||
|
width: 640px;
|
||||||
|
height: 420px;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-image: url('/images/landing/spacedeck-screenshot1.jpg');
|
||||||
|
background-color: white;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 40px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#legal {
|
||||||
|
.landing-box {
|
||||||
|
width: 800px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
margin-left: 300px;
|
padding: 40px;
|
||||||
margin-top: 100px;
|
padding-bottom: 80px;
|
||||||
margin-bottom: 100px;
|
text-align: center;
|
||||||
|
color: $medium;
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 1000px) {
|
|
||||||
#landing {
|
@media screen and (min-width: 801px) {
|
||||||
section {
|
.plans-table-mobile {
|
||||||
margin-left: 20px;
|
display: none;
|
||||||
margin-right: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.footer {
|
|
||||||
margin-left: 20px;
|
@media screen and (max-width: 800px) {
|
||||||
margin-right: 20px;
|
|
||||||
|
ul.lead.lead-xl, p.lead.lead-xl, ol.lead.lead-xl {
|
||||||
|
font-size: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-right {
|
.header-right {
|
||||||
right: auto;
|
> span:first-child {
|
||||||
padding-left: 10px;
|
display: none;
|
||||||
padding-right: 20px;
|
}
|
||||||
padding-top: 80px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#folder-wrapper {
|
.plans-table {
|
||||||
padding-top: 128px;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-table-mobile {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, li {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
//@include transition( all 0.25s ease-in-out);
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 1160px;
|
max-width: 1160px;
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
margin-left: 44px;
|
position: fixed;
|
||||||
margin-bottom: 44px;
|
margin: 44px 44px;
|
||||||
.icon {display: block; }
|
.icon {display: block; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +135,7 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@include user-select(none);
|
||||||
|
|
||||||
border-radius: $radius*3;
|
border-radius: $radius*3;
|
||||||
background-color: $light !important;
|
background-color: $light !important;
|
||||||
@ -145,6 +146,7 @@
|
|||||||
.modal-header {
|
.modal-header {
|
||||||
padding: 30px 40px;
|
padding: 30px 40px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-search {
|
.close-search {
|
||||||
@ -277,5 +279,25 @@
|
|||||||
|
|
||||||
// Footer (for actions)
|
// Footer (for actions)
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
margin-top: 20px;
|
// border-bottom-left-radius: $radius;
|
||||||
|
// border-bottom-right-radius: $radius;
|
||||||
|
// background-color: $dark !important;
|
||||||
|
// padding: 40px;
|
||||||
|
// padding-top: 0px;
|
||||||
|
// text-align: right; // right align buttons
|
||||||
|
@include clearfix(); // clear it in case folks use .pull-* classes on buttons
|
||||||
|
|
||||||
|
// Properly space out buttons
|
||||||
|
// .btn + .btn {
|
||||||
|
// margin-left: 5px;
|
||||||
|
// margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
|
||||||
|
// }
|
||||||
|
// // but override that for button groups
|
||||||
|
// .btn-group .btn + .btn {
|
||||||
|
// margin-left: -1px;
|
||||||
|
// }
|
||||||
|
// // and override it for block buttons as well
|
||||||
|
// .btn-block + .btn-block {
|
||||||
|
// margin-left: 0;
|
||||||
|
// }
|
||||||
}
|
}
|
12
styles/normalize.scss
vendored
12
styles/normalize.scss
vendored
@ -1,5 +1,17 @@
|
|||||||
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */
|
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */
|
||||||
|
|
||||||
|
//
|
||||||
|
// 1. Set default font family to sans-serif.
|
||||||
|
// 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||||
|
// user zoom.
|
||||||
|
//
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; // 1
|
||||||
|
-ms-text-size-adjust: 100%; // 2
|
||||||
|
-webkit-text-size-adjust: 100%; // 2
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Remove default margin.
|
// Remove default margin.
|
||||||
//
|
//
|
||||||
|
@ -27,5 +27,6 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
z-index: 800;
|
z-index: 800;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
opacity: 0.25;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,22 @@
|
|||||||
li {
|
li {
|
||||||
&.checked {
|
&.checked {
|
||||||
|
|
||||||
|
&:before {background-color: $medium !important; }
|
||||||
> a,
|
> a,
|
||||||
> span {
|
> span {
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
&:before {background-color: $medium; }
|
||||||
> a,
|
> a,
|
||||||
> span {
|
> span {
|
||||||
background-color: rgba(0,0,0,0.025) !important;
|
background-color: rgba(0,0,0,0.025) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:before {background-color: $medium; }
|
||||||
> a,
|
> a,
|
||||||
> span {
|
> span {
|
||||||
color: $medium;
|
color: $medium;
|
||||||
@ -41,14 +45,17 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-webkit-mask-image: -webkit-gradient(linear, left top, left 15px, from(rgba(0,0,0,0)), to(rgba(0,0,0,0.5)));
|
||||||
|
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
//font-size: 15px;
|
font-size: 15px;
|
||||||
//line-height: 14px;
|
line-height: 14px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
// background-color: $dark;
|
// background-color: $dark;
|
||||||
|
color: $medium;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
|
|
||||||
.divider + li span {border: none !important; }
|
.divider + li span {border: none !important; }
|
||||||
@ -83,11 +90,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: black;
|
// background-color: rgba(0,0,0,0.025);
|
||||||
|
&:before {
|
||||||
|
background-color: $medium;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
> a,
|
> a,
|
||||||
> span {
|
> span {
|
||||||
color: white;
|
color: $medium;
|
||||||
|
color: $dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +126,9 @@
|
|||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
color: $medium;
|
||||||
margin: 0 25px;
|
margin: 0 25px;
|
||||||
padding: 10px 0px;
|
padding: 16px 3px;
|
||||||
// line-height: 50px;
|
// line-height: 50px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
border: 4px solid black;
|
border: 1px dotted rgba(128,128,128,0.5);
|
||||||
|
|
||||||
transition-duration: 0.25s;
|
transition-duration: 0.25s;
|
||||||
transition-property: width, height, background-color;
|
transition-property: width, height, background-color;
|
||||||
@ -132,27 +132,32 @@
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
|
/** {
|
||||||
|
-moz-user-select: none !important; // firefox has selection problems
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.snap-ruler-h {
|
.snap-ruler-h {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2000;
|
z-index: 0;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: black;
|
background-color: rgba(0,0,0,0.5);
|
||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snap-ruler-v {
|
.snap-ruler-v {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2000;
|
z-index: 0;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background-color: black;
|
background-color: rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor {
|
.cursor {
|
||||||
@ -222,12 +227,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#space {
|
#space {
|
||||||
// user-select: all;
|
/*-webkit-user-select: all;
|
||||||
|
-ms-user-select: all;
|
||||||
|
-moz-user-select: all;
|
||||||
|
user-select: all;*/
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
|
|
||||||
|
//padding-top: 64px !important;
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#made-with {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 12px;
|
||||||
|
opacity: 0.25;
|
||||||
|
a {color: $dark; }
|
||||||
|
p {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#baseline {
|
#baseline {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -275,8 +298,8 @@
|
|||||||
|
|
||||||
.space-bounds {
|
.space-bounds {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0px;
|
||||||
top: 0;
|
top: 0px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -65,15 +65,10 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height:100%;
|
height:100%;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: $black;
|
background-color: $light;
|
||||||
}
|
color: $darker;
|
||||||
|
|
||||||
body {
|
|
||||||
max-width: 100%;
|
|
||||||
padding: 0px;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*[contenteditable="true"] {
|
*[contenteditable="true"] {
|
||||||
@ -86,12 +81,70 @@ body {
|
|||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0px;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
//@include user-select(none);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.img img {
|
.img img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*.layer {
|
.plan {
|
||||||
|
color: $medium;
|
||||||
|
border-radius: $radius;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid rgba(0,0,0,0.05);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
color: black;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
font-size: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
border-top: 2px solid rgba(0,0,0,0.05);
|
||||||
|
padding-top: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
li {padding-top: 2px; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#startup {
|
||||||
|
background-position: center;
|
||||||
|
background-image:url(/images/diamond.svg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
#home {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer {
|
||||||
@include transition( all 0.2s ease-in-out);
|
@include transition( all 0.2s ease-in-out);
|
||||||
@include backface-visibility(hidden);
|
@include backface-visibility(hidden);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -119,7 +172,7 @@ body {
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
[draggable] {
|
[draggable] {
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
}
|
}
|
||||||
.table {
|
.table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
color: $medium;;
|
||||||
font-family: $main-font;
|
font-family: $main-font;
|
||||||
border-radius: $radius;
|
// border-radius: $radius;
|
||||||
border: 2px solid rgba(0,0,0,0.0125);
|
// border: 2px solid rgba(0,0,0,0.0125) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead > tr > th:first-child,
|
.table thead > tr > th:first-child,
|
||||||
|
@ -19,23 +19,50 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
//text-align: center;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 20px;
|
bottom: 0px;
|
||||||
|
//width: 100%;
|
||||||
z-index: 3000;
|
z-index: 3000;
|
||||||
padding: 0;
|
padding: $gutter-b;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
|
|
||||||
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
|
transition-duration: 0.15s;
|
||||||
border: 1px solid black;
|
transition-timing-function: ease-in-out;
|
||||||
border-radius: 5px;
|
transition-delay: initial;
|
||||||
|
transition-property: opacity, transform;
|
||||||
// FIXME questionable?
|
@include backface-visibility(hidden);
|
||||||
|
|
||||||
|
@include translate3d(0, 10px, 0);
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&.out {
|
||||||
|
@include translate3d(0, 10px, 0);
|
||||||
|
* {pointer-events: none !important; }
|
||||||
|
button, input, .dialog {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.in {
|
||||||
|
@include translate3d(0, 0, 0);
|
||||||
|
|
||||||
|
&.out {
|
||||||
|
@include translate3d(0, 10px, 0);
|
||||||
|
* {pointer-events: none !important; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
margin: 0 2px;
|
||||||
|
margin-top: 4px;
|
||||||
pointer-events: auto !important;
|
pointer-events: auto !important;
|
||||||
|
&.out {
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.toolbar-vertical {
|
&.toolbar-vertical {
|
||||||
@ -160,6 +187,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-properties {
|
.toolbar-properties {
|
||||||
|
bottom: 64px;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
|
||||||
&.in {
|
&.in {
|
||||||
@ -168,12 +196,12 @@
|
|||||||
|
|
||||||
.icon-sm {
|
.icon-sm {
|
||||||
z-index: 110;
|
z-index: 110;
|
||||||
//background-color: #222;
|
background-color: #222;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jewel {
|
.jewel {
|
||||||
border: 2px solid #888;
|
border: 2px solid rgba(255,255,255,0.5);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #989898;
|
color: #989898;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
@ -200,22 +228,5 @@
|
|||||||
|
|
||||||
.toolbar-elements > .btn-group,
|
.toolbar-elements > .btn-group,
|
||||||
.toolbar-properties > .btn-group {
|
.toolbar-properties > .btn-group {
|
||||||
//box-shadow: 0 0 30px rgba(0,0,0,0.5);
|
box-shadow: 0 0 30px rgba(0,0,0,0.5);
|
||||||
background-color: $white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-elements {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar-properties {
|
|
||||||
right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zoom-bar {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 30px;
|
|
||||||
right: 30px;
|
|
||||||
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@
|
|||||||
@include translate3d(0, 0, 0);
|
@include translate3d(0, 0, 0);
|
||||||
|
|
||||||
// @include backface-visibility(hidden);
|
// @include backface-visibility(hidden);
|
||||||
|
-webkit-perspective: 1000;
|
||||||
|
-moz-perspective: 1000;
|
||||||
|
-ms-perspective: 1000;
|
||||||
perspective: 1000;
|
perspective: 1000;
|
||||||
|
|
||||||
.panel-toggles {
|
.panel-toggles {
|
||||||
@ -96,6 +99,9 @@
|
|||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
// @include backface-visibility(hidden);
|
// @include backface-visibility(hidden);
|
||||||
|
-webkit-perspective: 1000;
|
||||||
|
-moz-perspective: 1000;
|
||||||
|
-ms-perspective: 1000;
|
||||||
perspective: 1000;
|
perspective: 1000;
|
||||||
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
@import "vars";
|
@import "vars";
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;900&display=swap');
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
color: $medium;
|
color: $medium;
|
||||||
@ -27,7 +25,7 @@ hr {
|
|||||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: 900;
|
font-weight: 500;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
@ -48,7 +46,8 @@ strong {font-weight: 500; }
|
|||||||
small {font-size: 75%; }
|
small {font-size: 75%; }
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: black;
|
text-decoration: none;
|
||||||
|
color: $medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
dl {
|
dl {
|
||||||
|
@ -9,6 +9,13 @@ $green: #2ecc71;
|
|||||||
$red: #ff5955;
|
$red: #ff5955;
|
||||||
$yellow: #f1c40f;
|
$yellow: #f1c40f;
|
||||||
|
|
||||||
|
$light: #f5f5f5;
|
||||||
|
$lightish: #eee;
|
||||||
|
|
||||||
|
|
||||||
|
$facebook: #3e5b97;
|
||||||
|
$twitter: #2aa7de;
|
||||||
|
|
||||||
$color-1 : #4a2f7e; // purple
|
$color-1 : #4a2f7e; // purple
|
||||||
$color-2 : #9b59b6; // lilac
|
$color-2 : #9b59b6; // lilac
|
||||||
$color-3 : #3498db; // blue
|
$color-3 : #3498db; // blue
|
||||||
@ -25,18 +32,15 @@ $black: #111; // black
|
|||||||
$darker: #292929;
|
$darker: #292929;
|
||||||
$dark: #222; // dark
|
$dark: #222; // dark
|
||||||
$medium: #888; // medium
|
$medium: #888; // medium
|
||||||
$light: #f5f5f5;
|
|
||||||
$lightish: #eee; // fixme
|
|
||||||
$lighter: #989898;
|
$lighter: #989898;
|
||||||
$white: #ffffff;
|
|
||||||
|
|
||||||
$sidebar-width: 280px;
|
$sidebar-width: 280px;
|
||||||
|
|
||||||
$main-font: Inter;
|
$main-font: Avenir W01;
|
||||||
$sec-font: Inter;
|
$sec-font: Avenir W01;
|
||||||
|
|
||||||
$font-size: 20px;
|
$font-size: 18px;
|
||||||
$line-height: 1.5em;
|
$line-height: 24px;
|
||||||
|
|
||||||
$gutter-a: 10px;
|
$gutter-a: 10px;
|
||||||
$gutter-b: 20px;
|
$gutter-b: 20px;
|
||||||
|
@ -1,34 +1,62 @@
|
|||||||
{% extends 'layouts/outer.html' %}
|
{% extends 'layouts/outer.html' %}
|
||||||
|
|
||||||
{% block title %}Spacedeck{% endblock %}
|
{% block title %}[[ __("welcome") ]]{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div id="landing">
|
<div id="landing">
|
||||||
<section>
|
<div class="landing-keyvisual-wrapper">
|
||||||
<h1>Work Together, Visually.</h1>
|
<div class="landing-box">
|
||||||
<p>
|
<h2>[[__("landing_title")]]</h2>
|
||||||
Whenever you need to lay out pictures, text notes, video and audio clips on a blank canvas,
|
|
||||||
Spacedeck can help you.
|
<p class="lead">
|
||||||
|
<a href="/signup" class="btn btn-primary btn-block btn-xl">[[__("signup")]]</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Spacedeck is a browser based application. It is the right tool for you if you want to quickly put together a collage of your idea or concept, either for yourself or to share it with teammembers, clients or students. Changes are updated in realtime.
|
<p class="lead">
|
||||||
|
<a href="/login" class="btn btn-primary btn-block btn-xl">[[__("login")]]</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Spacedeck is not meant for creating polished designs, but it is a good fit for:
|
<p class="lead">
|
||||||
|
[[__("landing_claim")]]
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p class="lead">
|
||||||
|
[[__("landing_example")]]
|
||||||
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Moodboards</li>
|
<li class="lead">
|
||||||
<li>Collages</li>
|
[[__("landing_features_1") | safe ]]
|
||||||
<li>Teaching (Virtual Blackboards)</li>
|
</li>
|
||||||
<li>Shared Whiteboards</li>
|
|
||||||
<li>Design Thinking</li>
|
<li class="lead">
|
||||||
|
[[__("landing_features_2") | safe ]]
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="lead">
|
||||||
|
[[__("landing_features_3") | safe ]]
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="lead">
|
||||||
|
[[__("landing_features_4") | safe ]]
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="lead">
|
||||||
|
[[__("landing_features_5") | safe ]]
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="lead">
|
||||||
|
[[__("landing_features_6") | safe ]]
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="lead">
|
||||||
|
[[__("landing_features_7") | safe ]]
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<img src="/images/sd6-screenshot.png" alt="Screenshot of Spacedeck 6.0">
|
</div>
|
||||||
<p>
|
</div>
|
||||||
The hosted version of Spacedeck 6.0 is currently in beta and invite only. You can also self-host and <a href="https://github.com/spacedeck/spacedeck-open">participate in the open source development</a>.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -9,25 +9,39 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<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' | cdn ]]" rel="icon" type="image/x-icon" />
|
||||||
|
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,700,600,800,300|Montserrat:400,700|EB+Garamond|Vollkorn|Lato|Roboto|Source+Code+Pro|Ubuntu|Raleway|Playfair+Display|Crimson+Text' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
|
<link type="text/css" rel="stylesheet" href="https://fast.fonts.net/cssapi/ee1a3484-4d98-4f9f-9f55-020a7b37f3c5.css"/>
|
||||||
|
|
||||||
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
|
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
|
||||||
<script> var csrf_token = '[[ csrf_token ]]'; </script>
|
<script> var csrf_token = '[[ csrf_token ]]'; </script>
|
||||||
|
|
||||||
<!--script src="[[ '/javascripts/jquery-2.1.4.min.js' | cdn ]]"></script-->
|
<script src="[[ '/javascripts/jquery-2.1.4.min.js' | cdn ]]"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<!--[if lt IE 10]>
|
||||||
|
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||||
|
<![endif]-->
|
||||||
<header id="landing-header" class="header">
|
<header id="landing-header" class="header">
|
||||||
<div class="header-left">
|
<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>
|
<a class="btn btn-transparent btn-nude" href="[[config.endpoint]]/"><img src="[[ '/images/sd5-logo.svg' | cdn ]]" width="190"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-right pull-right">
|
<div class="header-right pull-right">
|
||||||
{% if !user %}
|
{% if !user %}
|
||||||
|
<span class="btn-group dark round">
|
||||||
|
{% if (locale != "de") %}<a href="/t/de" rel="alternate" hreflang="de" class="btn btn-transparent btn-md">Deutsch</a>{% endif %}
|
||||||
|
{% if (locale != "en") %}<a href="/t/en" rel="alternate" hreflang="en" class="btn btn-transparent btn-md">English</a>{% endif %}
|
||||||
|
{% if (locale != "fr") %}<a href="/t/fr" rel="alternate" hreflang="fr" class="btn btn-transparent btn-md">Français</a>{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
<a class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a>
|
<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>
|
<a class="btn btn-md btn-blue btn-round" href="/signup">[[__("signup")]]</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-md btn-dark btn-round" href="/spaces">[[__("spaces")]]</a>
|
<a class="btn btn-md btn-blue btn-round" href="/spaces">[[__("spaces")]]</a>
|
||||||
<a class="btn btn-md btn-dark btn-round" href="/logout">[[__("logout")]]</a>
|
<a class="btn btn-md btn-dark btn-round" href="/logout">[[__("logout")]]</a>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -38,11 +52,8 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>
|
<p>
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
© 2020 <a href="https://mntre.com">MNT Research GmbH</a>, Fehlerstr. 8, 12161 Berlin, Germany<br>
|
<a href="/contact">[[ __("contact") ]]</a>
|
||||||
© 2011–2019 Spacedeck GmbH (in liquidation)<br>
|
<span style="color:#888">© 2011–2017 The Spacedeck Open Developers</span>
|
||||||
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>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user