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
Showing with 539 additions and 946 deletions
+539 -946
......@@ -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