Commit b85aa245 authored by Wolfgang Knopki's avatar Wolfgang Knopki
Browse files

Merge branch 'mnt' into 'master'

Mnt

See merge request !2
parents 2fc14e1e fff0340f
......@@ -138,7 +138,6 @@ router.get('/', function(req, res, next) {
"$exists": 1
}
}).populate("space").exec(function(err, memberships) {
async.map(memberships, function(membership, memcb) {
Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) {
cb(null, spaces.map(function(s) {
......
......@@ -51,8 +51,7 @@ router.get('/png', function(req, res, next) {
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) {
phantom.takeScreenshot(req.space, "png", function(local_path) {
var localResizedFilePath = local_path + ".thumb.jpg";
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
......@@ -79,14 +78,14 @@ router.get('/png', function(req, res, next) {
var oldPath = url.parse(oldUrl).pathname;
uploader.removeFile(oldPath, function(err, res) {});
}
fs.unlink(local_path);
fs.unlinkSync(local_path);
} catch (e) {
console.error(e);
}
});
try {
fs.unlink(localResizedFilePath);
fs.unlinkSync(localResizedFilePath);
} catch (e) {
console.error(e);
}
......@@ -95,7 +94,7 @@ router.get('/png', function(req, res, next) {
},
function() {
// on_error
console.error("phantom could not create screenshot for space " + req.space_id);
console.error("[space screenshot] could not create screenshot for space " + req.space_id);
res.status(404).send("Not found");
});
} else {
......
......@@ -45,10 +45,12 @@ router.post('/', function(req, res, next) {
"email": membership.email_invited
}}).then(function(user) {
// existing user? then immediately activate membership
if (user) {
membership.user_id = user._id;
membership.state = "active";
} else {
// if not, invite via email and invite code
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
}
......@@ -84,13 +86,13 @@ router.post('/', function(req, res, next) {
} else {
res.status(400).json({
"error": "user already in space"
"error": "This email is already included in the Space memberships."
});
}
} else {
res.status(403).json({
"error": "not_permitted"
"error": "Only administrators can do that."
});
}
});
......@@ -102,12 +104,19 @@ router.put('/:membership_id', function(req, res, next) {
_id: req.params.membership_id
}}).then(function(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;
mem.role = attrs.role;
mem.save(function() {
res.status(201).json(mem);
});
}
}
});
} else {
res.sendStatus(403);
......@@ -118,13 +127,25 @@ router.put('/:membership_id', function(req, res, next) {
});
router.delete('/:membership_id', function(req, res, next) {
if (req.user) {
if (req.user && req.spaceRole == 'admin') {
db.Membership.count({ where: {
space_id: req.space._id,
role: "admin"
}}).then(function(adminCount) {
db.Membership.findOne({ where: {
_id: req.params.membership_id
}}).then(function(mem) {
// deleting an admin? need at least 1
if (mem.role != "admin" || adminCount > 1) {
mem.destroy().then(function() {
res.sendStatus(204);
});
} else {
res.status(400).json({
"error": "Space needs at least one administrator."
});
}
})
});
} else {
res.sendStatus(403);
......
......@@ -42,79 +42,62 @@ var spaceMapping = {
thumbnail_url: 1
};
router.get('/', function(req, res, next) {
if (!req.user) {
res.status(403).json({
error: "auth required"
});
} else {
if (req.query.writablefolders) {
db.Membership.find({where: {
user_id: req.user._id
}}, (memberships) => {
var validMemberships = memberships.filter((m) => {
if (!m.space_id || (m.space_id == "undefined"))
return false;
return true;
});
var editorMemberships = validMemberships.filter((m) => {
return (m.role == "editor") || (m.role == "admin")
});
var spaceIds = editorMemberships.map(function(m) {
return m.space_id;
});
// TODO port
var q = {
"space_type": "folder",
"$or": [{
"creator": req.user._id
}, {
"_id": {
"$in": spaceIds
},
"creator": {
"$ne": req.user._id
function listSpacesInFolder(req, res, parent_space_id) {
db.Space
.findOne({where: {
_id: parent_space_id
}})
.then(function(space) {
if (space) {
function spacesForRole(role) {
if (role == "none") {
if (space.access_mode == "public") {
role = "viewer";
}
}]
};
}
if (role != "none") {
db.Space
.findAll({where: q})
.findAll({where:{
parent_space_id: parent_space_id
}, include:[db.CreatorSafeInclude(db)]})
.then(function(spaces) {
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s; //.toObject();
return spaceObj;
res.status(200).json(spaces);
});
} else {
res.status(403).json({"error": "not authorized"});
}
}
async.map(spaces, (space, cb) => {
Space.getRecursiveSubspacesForSpace(space, (err, spaces) => {
var allSpaces = spaces;
cb(err, allSpaces);
})
}, (err, spaces) => {
if (req["spaceAuth"] && space.edit_hash) {
// TODO could be editor, too
spacesForRole("none");
} else {
db.getUserRoleInSpace(space, req.user, spacesForRole);
}
} else {
res.status(404).json({"error": "space not found"});
}
});
}
var allSpaces = _.flatten(spaces);
router.get('/', function(req, res, next) {
var onlyFolders = _.filter(allSpaces, (s) => {
return s.space_type == "folder";
})
var uniqueFolders = _.unique(onlyFolders, (s) => {
return s._id;
})
if (req.query.parent_space_id && req["spaceAuth"]) {
// list subspaces of a space authorized anonymously
listSpacesInFolder(req, res, req.query.parent_space_id);
return;
}
res.status(200).json(uniqueFolders);
});
});
if (!req.user) {
res.status(403).json({
error: "auth required"
});
} else if (req.query.search) {
} else {
if (req.query.search) {
db.Membership.findAll({where:{
user_id: req.user._id
}}).then(memberships => {
// search for spaces
var validMemberships = memberships.filter(function(m) {
if (!m.space_id || (m.space_id == "undefined"))
......@@ -133,7 +116,7 @@ router.get('/', function(req, res, next) {
{"_id": {[Op.in]: spaceIds}},
{"parent_space_id": {[Op.in]: spaceIds}}],
name: {[Op.like]: "%"+req.query.search+"%"}
}, include: ['creator']};
}, include: [db.CreatorSafeInclude(db)]};
db.Space
.findAll(q)
......@@ -143,39 +126,12 @@ router.get('/', function(req, res, next) {
});
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
// list spaces in a folder
db.Space
.findOne({where: {
_id: req.query.parent_space_id
}})
//.populate('creator', userMapping)
.then(function(space) {
if (space) {
db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") {
if (space.access_mode == "public") {
role = "viewer";
}
}
if (role != "none") {
db.Space
.findAll({where:{
parent_space_id: req.query.parent_space_id
}, include:['creator']})
.then(function(spaces) {
res.status(200).json(spaces);
});
listSpacesInFolder(req, res, req.query.parent_space_id);
} else {
res.status(403).json({"error": "no authorized"});
}
});
} else {
res.status(404).json({"error": "space not found"});
}
});
// list home folder and spaces/folders that the user is a member of
} else {
db.Membership.findAll({ where: {
user_id: req.user._id
}}).then(memberships => {
......@@ -184,6 +140,7 @@ router.get('/', function(req, res, next) {
var validMemberships = memberships.filter(function(m) {
if (!m.space_id || (m.space_id == "undefined"))
return false;
return true;
});
var spaceIds = validMemberships.map(function(m) {
......@@ -205,7 +162,7 @@ router.get('/', function(req, res, next) {
};
db.Space
.findAll({where: q, include: ['creator']})
.findAll({where: q, include: [db.CreatorSafeInclude(db)]})
.then(function(spaces) {
var updatedSpaces = spaces.map(function(s) {
var spaceObj = db.spaceToObject(s);
......@@ -227,15 +184,19 @@ router.post('/', function(req, res, next) {
attrs._id = uuidv4();
attrs.creator_id = req.user._id;
attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
attrs.edit_slug = slug(attrs.name);
attrs.edit_slug = attrs.edit_slug || slug(attrs.name);
attrs.access_mode = "private";
db.Space.create(attrs).then(createdSpace => {
//if (err) res.sendStatus(400);
res.status(201).json(createdSpace);
// create initial admin membership
var membership = {
_id: uuidv4(),
user_id: req.user._id,
space_id: attrs._id,
role: "admin"
role: "admin",
state: "active"
};
db.Membership.create(membership).then(() => {
......@@ -265,6 +226,7 @@ router.post('/', function(req, res, next) {
}
});
} else {
attrs.parent_space_id = req.user.home_folder_id;
createSpace();
}
......@@ -314,8 +276,17 @@ router.put('/:id', function(req, res) {
newAttr.edit_slug = slug(newAttr['name']);
delete newAttr['_id'];
delete newAttr['editor_name'];
delete newAttr['creator'];
delete newAttr['creator_id'];
delete newAttr['space_type'];
if (req['spaceRole'] != "admin") {
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
......@@ -362,43 +333,6 @@ router.post('/:id/background', function(req, res, next) {
});
});
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 duplicate"
});
} else {
db.getUserRoleInSpace(parentSpace, req.user, (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) {
if (req.user) {
const space = req.space;
......@@ -418,136 +352,4 @@ 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 = os.tmpdir() + "/" + 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 = os.tmpdir() + "/" + rawName;
fs.mkdir(outputFolder, function(err) {
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
// FIXME not portable
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((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 = db.Artifact.create({
_id: uuidv4(),
mime: "image/jpg",
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
w: w,
h: h,
x: x,
y: y,
z: (number + (count + 100))
}).then(a => {
payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
if (error) res.status(400).json(error);
else {
if (withZones) {
var zone = {
_id: uuidv4(),
mime: "x-spacedeck/zone",
description: "Zone " + (number),
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
w: artifact.w + 20,
h: artifact.h + 40,
x: x - 10,
y: y - 30,
z: number,
order: number,
valign: "middle",
align: "center"
};
db.Artifact.create(zone).then((z) => {
redis.sendMessage("create", "Artifact", z.toJSON(), req.channelId);
cb(null, [artifact, zone]);
});
} else {
cb(null, [artifact]);
}
}
});
});
});
}, function(err, artifacts) {
// FIXME not portable
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", JSON.stringify(a), req.channelId);
});
} else  {
redis.sendMessage("create", "Artifact", JSON.stringify(artifact_or_artifacts), req.channelId);
}
cb(null);
});
});
});
});
} else {
console.error("error:", error);
// FIXME not portable
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;
......@@ -11,7 +11,6 @@ var importer = require('../../helpers/importer');
var bcrypt = require('bcryptjs');
var crypto = require('crypto');
var swig = require('swig');
var async = require('async');
var _ = require('underscore');
var fs = require('fs');
......@@ -51,12 +50,18 @@ router.post('/', function(req, res) {
var nickname = req.body["nickname"];
var password = req.body["password"];
var password_confirmation = req.body["password_confirmation"];
var invite_code = req.body["invite_code"];
if (password_confirmation != password) {
res.status(400).json({"error":"password_confirmation"});
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;
......@@ -83,28 +88,31 @@ router.post('/', function(req, res) {
res.sendStatus(400);
})
.then(u => {
var homeSpace = {
var homeFolder = {
_id: uuidv4(),
name: req.i18n.__("home"),
space_type: "folder",
creator_id: u._id
};
db.Space.create(homeSpace)
db.Space.create(homeFolder)
.error(err => {
res.sendStatus(400);
})
.then(homeSpace => {
u.home_folder_id = homeSpace._id;
.then(homeFolder => {
u.home_folder_id = homeFolder._id;
u.save()
.then(() => {
res.status(201).json({});
mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), {
action: {
link: config.endpoint + "/confirm/" + u.confirmation_token,
name: req.i18n.__("confirm_action")
// home folder created,
// auto accept pending invites
db.Membership.update({
"state": "active"
}, {
where: {
"email_invited": u.email,
"state": "pending"
}
});
res.status(201).json({});
})
.error(err => {
res.status(400).json(err);
......@@ -119,7 +127,6 @@ router.post('/', function(req, res) {
db.User.findAll({where: {email: email}})
.then(users => {
if (users.length == 0) {
//var domain = email.slice(email.lastIndexOf('@')+1);
createUser();
} else {
res.status(400).json({"error":"user_email_already_used"});
......@@ -168,36 +175,35 @@ router.post('/:id/password', function(req, res, next) {
});
});
} else {
res.status(403).json({"error": "old password wrong"});
res.status(403).json({"error": "Please enter the correct current password."});
}
} else {
res.status(403).json({"error": "wrong user"});
res.status(403).json({"error": "Access denied."});
}
} else {
res.status(400).json({"error": "password_to_short"});
res.status(400).json({"error": "Please choose a new password with at least 6 characters."});
}
});
router.delete('/:id', (req, res, next) => {
const user = req.user;
if(user._id == req.params.id) {
if (user.account_type == 'email') {
if (user._id == req.params.id) {
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);
else res.sendStatus(204);
});
} else {
res.bad_request("password_incorrect");
res.bad_request("Please enter the correct current password.");
}
} else {
user.remove((err) => {
if (err) res.status(400).json(err);
else res.sendStatus(204);
});
}
res.status(403).json({error: "Access denied."});
}
else res.status(403).json({error: ""});
});
router.put('/:user_id/confirm', (req, res) => {
......@@ -253,13 +259,6 @@ 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) => {
const email = req.query.email;
db.User.findOne({where: {"email": email}}).then((user) => {
......@@ -289,15 +288,10 @@ router.post('/password_reset_requests/:confirm_token/confirm', function(req, res
if (user) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) {
user.password_hash = hash;
user.password_token = null;
user.save(function(err, updatedUser){
if (err) {
res.sendStatus(400);
} else {
user.save().then(function(updatedUser) {
res.sendStatus(201);
}
});
});
});
......@@ -315,19 +309,4 @@ router.post('/:user_id/confirm', function(req, res, next) {
res.sendStatus(201);
});
router.get('/:user_id/importables', function(req, res, next) {
glob('*.zip', function(err, files) {
res.status(200).json(files);
});
});
router.get('/:user_id/import', function(req, res, next) {
if (req.query.zip) {
res.send("importing");
importer.importZIP(req.user, req.query.zip);
} else {
res.sendStatus(400);
}
});
module.exports = router;
......@@ -9,13 +9,157 @@ const router = express.Router();
const mailer = require('../helpers/mailer');
const _ = require('underscore');
const fs = require('fs')
const SamlStrategy = require('passport-saml').Strategy
const passport = require('passport')
const Saml2js = require('saml2js');
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
// =========== PASSPORT =======
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
var samlStrategy = new SamlStrategy({
// URL that goes from the Identity Provider -> Service Provider
callbackUrl: config.path,
entryPoint: config.entryPoint,
issuer: config.issuer,
identifierFormat: null,
validateInResponseTo: false,
disableRequestedAuthnContext: true
},
function (profile, done) {
return done(null, {
id: profile.nameID,
idFormat: profile.nameIDFormat,
email: profile.email,
firstName: profile.givenName,
lastName: profile.sn
});
});
passport.use(samlStrategy);
// to generate Service Provider's XML metadata
router.get('/saml/metadata',
function(req, res) {
res.type('application/xml');
var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync('/cert/certificate.pem', 'utf8'));
res.status(200).send(spMetadata);
}
);
router.post('/saml/SSO', passport.authenticate('saml', { failureRedirect: '/login', failureFlash: true}), function(req, res){
const xmlResponse = req.body.SAMLResponse;
const parser = new Saml2js(xmlResponse);
const response = parser.toObject();
const email = response["mail"];
console.log(parser.toJSON());
console.log("Nickname "+ response["givenName"])
const nickname = response["givenName"];
//check, if user exists, if not create.
db.User.findAll({where: {email: email}})
.then(users => {
if (users.length == 0) {
crypto.randomBytes(16, function(ex, buf) {
var token = buf.toString('hex');
var u = {
_id: uuidv4(),
email: email,
account_type: "email",
nickname: nickname,
password_hash: "00000",
prefs_language: req.i18n.locale,
confirmation_token: token
};
db.User.create(u)
.error(err => {
res.sendStatus(400);
})
.then(u => {
var homeFolder = {
_id: uuidv4(),
name: req.i18n.__("home"),
space_type: "folder",
creator_id: u._id
};
db.Space.create(homeFolder)
.error(err => {
res.sendStatus(400);
})
.then(homeFolder => {
u.home_folder_id = homeFolder._id;
u.save()
.then(() => {
// home folder created,
// auto accept pending invites
db.Membership.update({
"state": "active"
}, {
where: {
"email_invited": u.email,
"state": "pending"
}
});
res.status(201).json({});
})
.error(err => {
res.status(400).json(err);
});
})
});
});
}
}).then(user =>{
db.User.findOne({where: {email: email}})
.error(err => {
res.sendStatus(404);
})
.then(user => {
crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex');
var session = {
user_id: user._id,
token: token,
ip: req.ip,
device: "web",
created_at: new Date(),
url : "/"
};
db.Session.create(session)
.error(err => {
console.error("Error creating Session:",err);
res.redirect(500, "/");
})
.then(() => {
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname;
console.log("session set successfully");
res.cookie('sdsession', token, { domain: domain, httpOnly: true });
res.redirect(302, "/")
});
});
});
});
});
router.get('/', (req, res) => {
res.render('index', { title: 'Spaces' });
res.render('index', { config:config, user:req.user });
});
router.get('/ping', (req, res) => {
......@@ -23,39 +167,35 @@ router.get('/ping', (req, res) => {
});
router.get('/spaces', (req, res) => {
res.render('spacedeck', { title: 'Spaces' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/not_found', (req, res) => {
res.render('not_found', { title: 'Spaces' });
res.render('not_found', {});
});
router.get('/confirm/:token', (req, res) => {
res.render('spacedeck', { title: 'Space' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/folders/:id', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/signup', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/accept/:id', (req, res) => {
res.render('spacedeck', {});
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/password-reset', (req, res) => {
res.render('spacedeck', { title: 'Signup' });
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/password-confirm/:token', (req, res) => {
res.render('spacedeck', { title: 'Signup' });
});
router.get('/team', (req, res) => {
res.render('spacedeck');
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/de/*', (req, res) => {
......@@ -74,44 +214,39 @@ router.get('/fr', (req, res) => {
res.redirect("/t/fr");
});
router.get('/en/*', (req, res) => {
res.redirect("/t/en");
router.get('/oc/*', (req, res) => {
res.redirect("/t/oc");
});
router.get('/en', (req, res) => {
res.redirect("/t/end");
router.get('/oc', (req, res) => {
res.redirect("/t/oc");
});
router.get('/it', (req, res) => {
router.get('/en/*', (req, res) => {
res.redirect("/t/en");
});
router.get('/account', (req, res) => {
res.render('spacedeck');
});
router.get('/login', (req, res) => {
res.render('spacedeck');
router.get('/en', (req, res) => {
res.redirect("/t/end");
});
router.get('/logout', (req, res) => {
router.get('/account', (req, res) => {
res.render('spacedeck');
});
router.get('/contact', (req, res) => {
res.render('public/contact');
});
router.get('/login', passport.authenticate('saml',
{
successRedirect: '/',
failureRedirect: '/login'
})
);
router.get('/about', (req, res) => {
res.render('public/about');
});
router.get('/terms', (req, res) => {
res.render('public/terms');
});
// res.render('spacedeck', { config:config, user:req.user });
//});
router.get('/privacy', (req, res) => {
res.render('public/privacy');
router.get('/logout', (req, res) => {
res.render('spacedeck', { config:config, user:req.user });
});
router.get('/t/:id', (req, res) => {
......@@ -123,22 +258,22 @@ router.get('/t/:id', (req, res) => {
res.redirect(path);
});
router.get('/s/:token', (req, res) => {
var token = req.params.token;
if (token.split("-").length > 0) {
token = token.split("-")[0];
router.get('/s/:hash', (req, res) => {
var hash = req.params.hash;
if (hash.split("-").length > 0) {
hash = hash.split("-")[0];
}
db.Space.findOne({where: {"edit_hash": token}}).then(function (space) {
db.Space.findOne({where: {"edit_hash": hash}}).then(function (space) {
if (space) {
if (req.accepts('text/html')){
res.redirect("/spaces/"+space._id + "?spaceAuth=" + token);
res.redirect("/spaces/"+space._id + "?spaceAuth=" + hash);
} else {
res.status(200).json(space);
}
} else {
if (req.accepts('text/html')) {
res.status(404).render('not_found', { title: 'Page Not Found.' });
res.status(404).render('not_found', {});
} else {
res.status(404).json({});
}
......@@ -147,7 +282,7 @@ router.get('/s/:token', (req, res) => {
});
router.get('/spaces/:id', (req, res) => {
res.render('spacedeck', { title: 'Space' });
res.render('spacedeck', { config:config, user:req.user });
});
module.exports = router;
module.exports = {router: router, passport:passport};
......@@ -16,37 +16,29 @@ 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"],
locales: ["en", "de", "fr", "oc", "es"],
defaultLocale: "en",
cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development')
});
swig.setDefaults({
varControls: ["[[", "]]"] // otherwise it's not compatible with vue.js
});
swig.setFilter('cdn', function(input, idx) {
return input;
});
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('view engine', 'ejs');
if (isProduction) {
app.set('views', path.join(__dirname, 'build', 'views'));
......@@ -68,18 +60,18 @@ app.use(bodyParser.urlencoded({
}));
app.use(cookieParser());
app.use(helmet.frameguard())
app.use(helmet.xssFilter())
app.use(helmet.hsts({
//app.use(helmet.frameguard())
//app.use(helmet.xssFilter())
/*app.use(helmet.hsts({
maxAge: 7776000000,
includeSubdomains: true
}))
includeSubDomains: true
}))*/
app.disable('x-powered-by');
app.use(helmet.noSniff())
//app.use(helmet.noSniff())
//app.use(require("./middlewares/error_helpers"));
app.use(require("./middlewares/session"));
//app.use(require("./middlewares/cors"));
app.use(require("./middlewares/session"));
app.use(require("./middlewares/i18n"));
app.use("/api", require("./middlewares/api_helpers"));
app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
......@@ -99,7 +91,7 @@ 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'), {
......@@ -111,20 +103,27 @@ if (config.get('storage_local_path')) {
//app.use(require('./middlewares/404'));
if (app.get('env') == 'development') {
app.set('view cache', false);
swig.setDefaults({cache: false});
} else {
app.use(require('./middlewares/500'));
}
const root = require('./routes/root');
const passport = root.passport;
app.use(passport.initialize());
app.use(passport.session());
app.use('/', root.router);
module.exports = app;
// CONNECT TO DATABASE
db.init();
// START WEBSERVER
const port = 9666;
const host = config.get('host');
const port = config.get('port');
const server = http.Server(app).listen(port, () => {
const server = http.Server(app).listen(port, host, () => {
if ("send" in process) {
process.send('online');
......
......@@ -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";
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";
opacity: 0.25;
}*/
......@@ -469,11 +469,10 @@
color: black;
//@include user-select(none);
white-space: normal;
font-size: 18px;
font-size: 36px;
&.artifact-zone {
border: 1px solid rgba(46,204,113,1);
background-color: rgba(46,204,113,0.025);
background-color: rgba(0,0,0,0.05);
border-radius: 10px;
&:after {display: none; }
.shape {display: none; }
......@@ -553,6 +552,10 @@ body:not(.present-mode) {
cursor: grab !important;
}
.tool-note {
cursor: crosshair !important;
}
.artifact.state-idle {
.progress, .progress-text {
display: none;
......
......@@ -7,12 +7,6 @@
.btn-group.colors {
.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);
}
}
......@@ -64,7 +58,7 @@
backface-visibility: hidden;
cursor: pointer;
background-color: $light;
color: $medium;;
color: $black;
@include user-select(none);
&:last-child {border: none;}
......@@ -82,12 +76,9 @@
&.btn-link {
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 {
border-radius: 100px !important;
}
......@@ -96,21 +87,10 @@
border-radius: 6px !important;
}
// &.close {
// position: absolute;
// top: 15px;
// right: 15px;
// z-index: 4000;
// font-size: 40px;
// }
&.btn-nude {
min-width: 0 !important;
// font-size: inherit !important;
padding: 0 !important;
// height: auto !important;
background-color: transparent;
color: $medium;
}
&.btn-nude + .btn-nude {
......@@ -123,7 +103,7 @@
&.btn-stroke {
box-shadow: inset 0 0 0 1px $dark;
color: $dark !important;
color: $black;
background-color: transparent;
&:active {
box-shadow: inset 0 0 0 1px white;
......@@ -132,9 +112,8 @@
}
&.btn-stroke-darken {
//box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
border: 1px solid rgba(0,0,0,0.1);
color: $medium;
border: 1px solid $black;
color: $black;
background-color: transparent;
&:active {
//box-shadow: inset 0 0 0 1px $dark;
......@@ -263,9 +242,18 @@
&.btn-transparent {
background-color: transparent;
color: $medium;
&.active {color: $darker !important; }
&.open {color: white !important; }
color: $dark;
&.active {
//color: $black !important;
color: $white;
background-color: $black;
}
&.open {
//color: $black !important;
color: $white;
background-color: $black;
border-radius: 0;
}
}
&.btn-transparent-medium {
......@@ -313,7 +301,7 @@
&.btn-dark {
background-color: $dark ;
color: $medium;
color: $white;
}
&.btn-medium {
......@@ -481,7 +469,6 @@
&.btn-icon {
padding: 0px !important;
font-weight: bold;
max-width: 60px;
&.btn-xl { max-width: 80px; }
......@@ -508,30 +495,6 @@
}
}
&.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 {
.icon:before {
line-height: 29px;
......@@ -567,7 +530,6 @@
.icon:before {line-height: 42px; }
.icon-label {
font-size: 11px;
text-transform: capitalize;
text-align: center;
margin: 8px 0;
display: block;
......@@ -580,7 +542,7 @@
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 0px;
font-weight: bold;
font-weight: 300;
}
&.hover {
......@@ -714,7 +676,6 @@
}
> * {
border-radius: 0 !important;
background-clip: padding-box;
width: 100%;
float: left;
......@@ -775,7 +736,7 @@
}
}
.btn-group {
@include scale(0,0);
//@include scale(0,0);
//@include transition( all 0.1s 0s ease-in-out);
position: absolute;
......@@ -787,7 +748,7 @@
margin-left: -12px;
.btn {
@include scale(0,0);
//@include scale(0,0);
//@include transition( all 0.1s 0.05s ease-in-out);
......@@ -979,31 +940,7 @@
}
}
.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;
......@@ -1014,13 +951,16 @@
vertical-align: middle;
white-space: nowrap;
//border: 1px solid $dark;
border-radius: 5px;
&.dark {
border-radius: $radius;
background-color: $dark;
color: $lighter;
color: $white;
.btn {
color: $lighter;
color: $white;
}
}
......
......@@ -96,15 +96,14 @@
border-bottom-right-radius: $radius*3;
}
.dialog-account {
width: 600px;
margin: auto;
margin-top: 100px;
}
.dialog {
font-size: 13px;
ol, ul, p {
font-size: inherit;
}
> .btn-block:last-child {
border-top-left-radius: 0px;
border-top-right-radius: 0px;
......@@ -112,24 +111,21 @@
border-bottom-right-radius: $radius*3;
}
min-width: 200px;
@include backface-visibility(hidden);
white-space: normal;
z-index: 1000;
position: absolute;
// white-space: normal;
font-size: 15px;
border: 1px solid black;
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
border-radius: 5px;
white-space: normal;
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;
background-color: $light;
color: $medium;
&.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);
color: $dark;
&.dark {
background-color: $dark;
}
.dialog-tabs-wrapper {
overflow: hidden;
......@@ -150,15 +146,13 @@
&:hover span {color: $dark; }
&.open span {
background-color: $light;
background-color: white;
color: $dark;
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-left-radius: 0px !important;
border-top-left-radius: $radius*3;
border-top-right-radius: $radius*3;
}
&:first-child span {
......@@ -200,7 +194,6 @@
text-align: center;
}
.dialog-section {
&:first-child {border: none !important; }
border-top: 2px solid rgba(0,0,0,0.1);
......@@ -228,4 +221,13 @@
h4 .icon {
height: 38px;
}
// account dialog
&.dialog-freestanding {
margin: auto;
position: relative;
top: 150px;
border: none;
width: 800px;
}
}
......@@ -43,9 +43,6 @@ $predelay: 0;
&.hover:hover,
&.open {
// &:before {opacity: 0.125; }
// pointer-events: auto;
background-color: $dark;
background-color: $light;
> * {
......@@ -111,8 +108,8 @@ $predelay: 0;
}
&:last-child > .btn{
border-top-right-radius: $radius ;
border-bottom-right-radius: $radius ;
border-top-right-radius: $radius;
border-bottom-right-radius: $radius;
}
}
}
......@@ -122,6 +119,10 @@ $predelay: 0;
position: relative;
vertical-align: middle;
a {
text-decoration: none;
}
&.dropdown-block {
display: block;
.dropdown-toggle {
......@@ -143,8 +144,7 @@ $predelay: 0;
&.light > .dropdown-menu,
&.light > .dialog {
background: $light;
color: $medium;
background: white;
}
> .dropdown-menu {
......@@ -189,8 +189,6 @@ $predelay: 0;
}
}
&.hover:hover > .dialog,
&.hover:hover > .dropdown-menu,
......@@ -206,9 +204,7 @@ $predelay: 0;
&.open {
> .dialog,
> .dropdown-menu {
-webkit-transform: translate3d(-50%, -50%, 100px) scale(1);
-ms-transform: translate3d(-50%, -50%, 100px) scale(1);
transform: translate3d(-50%, -50%, 100px) scale(1);
//transform: translate3d(-50%, -50%, 100px) scale(1);
}
}
......@@ -217,10 +213,8 @@ $predelay: 0;
left: 50%;
top: 50%;
margin-top: 0px;
@include transform-origin(center center);
-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);
//@include transform-origin(center center);
//transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
}
}
......@@ -230,10 +224,8 @@ $predelay: 0;
top: auto;
bottom: 100%;
margin-bottom: 16px;
@include transform-origin(bottom left);
-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);
//@include transform-origin(bottom left);
//transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
}
}
......@@ -243,10 +235,8 @@ $predelay: 0;
top: auto;
bottom: 100%;
margin-bottom: 16px;
@include transform-origin(bottom center);
-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);
//@include transform-origin(bottom center);
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
}
}
......@@ -257,10 +247,16 @@ $predelay: 0;
top: 100%;
bottom: auto;
margin-top: -16px;
@include transform-origin(top center);
-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);
//@include transform-origin(top center);
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
}
}
&.top.left {
> .dialog,
> .dropdown-menu {
left: 70px;
margin-top: -60px;
}
}
......@@ -270,20 +266,18 @@ $predelay: 0;
top: 100%;
bottom: auto;
left: auto;
right: 0;
margin-top: 16px;
@include transform-origin(top right);
-webkit-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
-ms-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
right: 70px;
margin-top: -60px;
//@include transform-origin(top right);
//transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
}
&.hover:hover,
&.open {
> .dialog,
> .dropdown-menu {
-webkit-transform: translate3d(0%, 0%, 100px) scale(1);
-ms-transform: translate3d(0%, 0%, 100px) scale(1);
transform: translate3d(0%, 0%, 100px) scale(1);
//transform: translate3d(0%, 0%, 100px) scale(1);
}
}
......@@ -312,9 +306,7 @@ $predelay: 0;
> .dialog,
> .dropdown-menu {
-webkit-transform: translate3d(-50%, 0%, 100px) scale(1);
-ms-transform: translate3d(-50%, 0%, 100px) scale(1);
transform: translate3d(-50%, 0%, 100px) scale(1);
//transform: translate3d(-50%, 0%, 100px) scale(1);
}
}
}
......@@ -324,9 +316,7 @@ $predelay: 0;
&.open {
> .dialog,
> .dropdown-menu {
-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;
//transform: translate3d(-33%, 0%, 100px) scale(1) !important;
}
}
}
......@@ -334,7 +324,7 @@ $predelay: 0;
.dropdown {
&.options-3 {
/*&.options-3 {
&.option-1:after { margin-left: -68px;}
&.option-2:after { margin-left: -8px;}
&.option-3:after { margin-left: 52px;}
......@@ -348,8 +338,9 @@ $predelay: 0;
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
}
}*/
/*
&:after {
@include transition( all 0.1s ease-in-out 0s);
content: "";
......@@ -362,26 +353,24 @@ $predelay: 0;
margin-left: -8px;
pointer-events: none !important;
left: 50%;
-webkit-transform: scale(0,0);
-ms-transform: scale(0,0);
transform: scale(0,0);
//transform: scale(0,0);
}
&.bottom:after, &.bottomleft:after {
@include transform-origin(bottom center);
//@include transform-origin(bottom center);
bottom: 100%;
border-bottom: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #303030;
border-left: 8px solid transparent;
}
&.top:after {
*/
/*&.top:after {
@include transform-origin(top center);
top: 100%;
border-bottom: 8px solid #303030;
border-right: 8px solid transparent;
border-top: 8px solid transparent;
border-left: 8px solid transparent;
}
}*/
}
......@@ -254,7 +254,6 @@
// word-wrap: break-word;
.item {
box-shadow: 0 0 1pxrgba(0,0,0,0.1);
display: inline-block;
text-align: left;
padding-right: $folder-gutter*2;
......@@ -397,7 +396,10 @@
&:active { opacity: 0.95 !important; }
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.025), 0 2px 7px rgba(0, 0, 0, 0.025);
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
border: 1px solid black;
// ???
@include opacity(1);
color: $medium;
// color: white;
......@@ -476,7 +478,6 @@
left: 0px;
z-index: 100;
width: auto;
background-color: rgba(255,255,255,1);
.dropdown {
position: absolute;
......@@ -501,30 +502,6 @@
color: $dark;
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 {
......
......@@ -28,7 +28,6 @@
line-height: 1.5;
width: 100%;
text-align: left;
color: $medium;
font-weight: normal;
cursor: pointer;
border-radius: $radius;
......
......@@ -2,24 +2,14 @@
@import "mixins";
.input-select {
// background-color: rgba(255,255,255,0.04);
// background-image: url('images/select_arrow.gif');
background-color: rgba(255,255,255,0.04);
background-image: url('images/select_arrow.gif');
border-radius: $radius;
display: inline-block;
width: 100%;
}
@-moz-document url-prefix() {
select.input{
background-repeat: no-repeat;
background-position: right center;
cursor: pointer;
}
}
select {
-webkit-appearance:none;
// -moz-appearance:window;
appearance:none;
padding-left: 0px;
width: 100%;
......
......@@ -23,7 +23,6 @@ input:invalid {
top: 0;
right: 0;
line-height: 1;
font-size: 10px;
margin: 12px;
color: red;
margin-right: 25px;
......@@ -113,43 +112,26 @@ select {
&.input-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);
}
&.input-light {
background-color: $light;
color: $medium;
}
&.input-dark {
background-color: $darker;
color: $medium;
}
&.input-lighten {
background-color: rgba(255,255,255,0.05);
color: $medium !important;
}
&.input-darken {
background-color: rgba(0,0,0,0.05);
color: $medium;
}
&.input-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();
......
......@@ -69,26 +69,27 @@
}
.handles {
// background-color: rgba(40,140,215,0.45);
border: 1px solid rgba(255,255,255,0.5);
//border: 1px solid rgba(255,255,255,0.5);
position: absolute;
left: 0;
top: 0;
bottom: 0;
bottom: -1;
right: 0;
z-index: 800;
pointer-events: none;
background: rgba(255,255,255,0.1);
&:after{
border: 1px dotted rgba(40,140,215,1);
border: 4px dotted #000000;
content: "";
display: block;
position: absolute;
height: auto;
width: auto;
top: -1px;
left: -1px;
right: -1px;
top: 0px;
left: 0px;
right: 0px;
bottom: -1px;
}
}
......@@ -97,7 +98,7 @@
border: 8px solid rgba(255,255,255,0.5);
&:after{
border: 8px dotted rgba(40,140,215,1);
border: 8px dotted #000000;
top: -4px;
left: -4px;
right: -4px;
......@@ -332,15 +333,14 @@
pointer-events:auto;
z-index: 2000;
position: absolute;
width: 30px !important;
height: 30px !important;
border-radius: 100%;
margin: -15px;
border: 1px solid rgba(0,0,0,0.25);
border: 1px solid black;
margin: -5px;
padding: 4px;
&:hover {
background-color: rgba(255,255,255,0.5);
background-color: black;
cursor: move;
}
}
......@@ -428,14 +428,7 @@
border-style: solid;
border-width: 10px;
border-color: transparent;
-webkit-background-clip: padding-box;
-moz-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;
}
......
......@@ -5,7 +5,6 @@
.header-left,
.header-right {
position: absolute;
//@include transition( all 0.25s ease-in-out);
@include backface-visibility(hidden);
z-index: 3000;
top: 10px;
......@@ -27,21 +26,21 @@
.home {
margin-top: -20px;
margin-left: -20px;
// .icon {color: $dark; }
}
.header-left {
@include transform-origin(center left);
left: 0;
padding-left: 10px;
padding-left: 20px;
padding-top: 20px;
}
.header-right {
@include transform-origin(center right);
right: 0;
padding-right: 10px;
padding-right: 20px;
padding-top: 20px;
}
.header-center {
@include transform-origin(center center);
width: 100%;
left: 0;
right: 0;
......@@ -56,7 +55,7 @@
}
}
.header-left > * { margin-right: 10px; }
.header-right > * { margin-left: 5px; }
.header-right > * { margin-left: 10px; }
.header-right { font-size: 0;}
.title {
......@@ -90,21 +89,3 @@
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,3 +85,12 @@
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);
}
@import "vars";
#landing-header {
background-color: rgba(255,255,255,0.3);
background-color: white;
height: 64px;
position: absolute;
position: relative;
top: 0;
left: 0;
right: 0;
}
.landing-keyvisual-wrapper {
background-image: url("../images/sd5-keyvisual-compressed.jpg");
background-size: cover;
background-position: center;
padding-top: 40px;
padding-bottom: 40px;
}
.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;
}
#landing {
margin-top: 100px;
ul {
margin: 0 !important;
padding: 0 !important;
}
section {
margin-left: 300px;
.upgrade-buttons {
text-align:center;
margin-top:20px;
> * {
max-width: 600px;
}
}
.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 {
padding: 40px;
padding-bottom: 80px;
text-align: center;
color: $medium;
margin-left: 300px;
margin-top: 100px;
margin-bottom: 100px;
}
a {
@media screen and (max-width: 1000px) {
#landing {
section {
margin-left: 20px;
margin-right: 20px;
}
}
@media screen and (min-width: 801px) {
.plans-table-mobile {
display: none;
}
}
@media screen and (max-width: 800px) {
ul.lead.lead-xl, p.lead.lead-xl, ol.lead.lead-xl {
font-size: 20px !important;
.footer {
margin-left: 20px;
margin-right: 20px;
}
.header-right {
> span:first-child {
display: none;
}
}
.plans-table {
display: none;
}
.plans-table-mobile {
display: block;
tbody {
display: block;
width: 100%;
}
tr {
display: block;
width: 100%;
}
td, th {
display: block;
width: 100%;
right: auto;
padding-left: 10px;
padding-right: 20px;
padding-top: 80px;
}
ul, li {
width: 100%;
}
#folder-wrapper {
padding-top: 128px;
}
}
......@@ -2,7 +2,6 @@
@import "mixins";
.wrapper {
//@include transition( all 0.25s ease-in-out);
position: relative;
margin: auto;
max-width: 1160px;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment