Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

pull/1/head
ansuz 7 years ago
commit 044384a576

@ -1,74 +1,10 @@
define(function() { /*
var config = {}; * You can override the configurable values from this file.
* The recommended method is to make a copy of this file (/customize.dist/application_config.js)
/* Select the buttons displayed on the main page to create new collaborative sessions in a 'customize' directory (/customize/application_config.js).
* Existing types : pad, code, poll, slide * If you want to check all the configurable values, you can open the internal configuration file
*/ but you should not change it directly (/common/application_config_internal.js)
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file', 'todo', 'contacts']; */
config.registeredOnlyTypes = ['file', 'contacts']; define(['/common/application_config_internal.js'], function (AppConfig) {
return AppConfig;
/* Cryptpad apps use a common API to display notifications to users
* by default, notifications are hidden after 5 seconds
* You can change their duration here (measured in milliseconds)
*/
config.notificationTimeout = 5000;
config.disableUserlistNotifications = false;
config.hideLoadingScreenTips = false;
config.enablePinning = true;
config.whiteboardPalette = [
'#000000', // black
'#FFFFFF', // white
'#848484', // grey
'#8B4513', // saddlebrown
'#FF0000', // red
'#FF8080', // peach?
'#FF8000', // orange
'#FFFF00', // yellow
'#80FF80', // light green
'#00FF00', // green
'#00FFFF', // cyan
'#008B8B', // dark cyan
'#0000FF', // blue
'#FF00FF', // fuschia
'#FF00C0', // hot pink
'#800080', // purple
];
config.enableTemplates = true;
config.enableHistory = true;
/* user passwords are hashed with scrypt, and salted with their username.
this value will be appended to the username, causing the resulting hash
to differ from other CryptPad instances if customized. This makes it
such that anyone who wants to bruteforce common credentials must do so
again on each CryptPad instance that they wish to attack.
WARNING: this should only be set when your CryptPad instance is first
created. Changing it at a later time will break logins for all existing
users.
*/
config.loginSalt = '';
config.minimumPasswordLength = 8;
config.badStateTimeout = 30000;
config.applicationsIcon = {
file: 'fa-file-text-o',
pad: 'fa-file-word-o',
code: 'fa-file-code-o',
slide: 'fa-file-powerpoint-o',
poll: 'fa-calendar',
whiteboard: 'fa-paint-brush',
todo: 'fa-tasks',
contacts: 'fa-users',
};
config.displayCreationScreen = false;
config.disableAnonymousStore = false;
return config;
}); });

@ -4,6 +4,7 @@
<head> <head>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title> <title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="description" content="CryptPad is an open-source browser-based suite of collaborative editors. It uses client-side encryption so that the server cannot read users' documents"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/> <link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script> <script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>

@ -3,10 +3,12 @@
"description": "realtime collaborative visual editor with zero knowlege server", "description": "realtime collaborative visual editor with zero knowlege server",
"version": "1.25.0", "version": "1.25.0",
"dependencies": { "dependencies": {
"chainpad-server": "^1.0.1", "chainpad-server": "^2.0.0",
"express": "~4.10.1", "express": "~4.10.1",
"nthen": "~0.1.0", "nthen": "~0.1.0",
"pull-stream": "^3.6.1",
"saferphore": "0.0.1", "saferphore": "0.0.1",
"stream-to-pull-stream": "^1.7.2",
"tweetnacl": "~0.12.2", "tweetnacl": "~0.12.2",
"ws": "^1.0.1" "ws": "^1.0.1"
}, },

@ -1,6 +1,11 @@
/*@flow*/
/* jshint esversion: 6 */
/* global Buffer */
var Fs = require("fs"); var Fs = require("fs");
var Path = require("path"); var Path = require("path");
var nThen = require("nthen"); var nThen = require("nthen");
const ToPull = require('stream-to-pull-stream');
const Pull = require('pull-stream');
var mkPath = function (env, channelId) { var mkPath = function (env, channelId) {
return Path.join(env.root, channelId.slice(0, 2), channelId) + '.ndjson'; return Path.join(env.root, channelId.slice(0, 2), channelId) + '.ndjson';
@ -8,7 +13,7 @@ var mkPath = function (env, channelId) {
var getMetadataAtPath = function (Env, path, cb) { var getMetadataAtPath = function (Env, path, cb) {
var remainder = ''; var remainder = '';
var stream = Fs.createReadStream(path, 'utf8'); var stream = Fs.createReadStream(path, { encoding: 'utf8' });
var complete = function (err, data) { var complete = function (err, data) {
var _cb = cb; var _cb = cb;
cb = undefined; cb = undefined;
@ -25,16 +30,16 @@ var getMetadataAtPath = function (Env, path, cb) {
var parsed = null; var parsed = null;
try { try {
parsed = JSON.parse(metadata); parsed = JSON.parse(metadata);
complete(void 0, parsed); complete(undefined, parsed);
} }
catch (e) { catch (e) {
console.log(); console.log("getMetadataAtPath");
console.error(e); console.error(e);
complete('INVALID_METADATA'); complete('INVALID_METADATA');
} }
}); });
stream.on('end', function () { stream.on('end', function () {
complete(null); complete();
}); });
stream.on('error', function (e) { complete(e); }); stream.on('error', function (e) { complete(e); });
}; };
@ -59,7 +64,7 @@ var closeChannel = function (env, channelName, cb) {
var clearChannel = function (env, channelId, cb) { var clearChannel = function (env, channelId, cb) {
var path = mkPath(env, channelId); var path = mkPath(env, channelId);
getMetadataAtPath(env, path, function (e, metadata) { getMetadataAtPath(env, path, function (e, metadata) {
if (e) { return cb(e); } if (e) { return cb(new Error(e)); }
if (!metadata) { if (!metadata) {
return void Fs.truncate(path, 0, function (err) { return void Fs.truncate(path, 0, function (err) {
if (err) { if (err) {
@ -87,7 +92,7 @@ var clearChannel = function (env, channelId, cb) {
var readMessages = function (path, msgHandler, cb) { var readMessages = function (path, msgHandler, cb) {
var remainder = ''; var remainder = '';
var stream = Fs.createReadStream(path, 'utf8'); var stream = Fs.createReadStream(path, { encoding: 'utf8' });
var complete = function (err) { var complete = function (err) {
var _cb = cb; var _cb = cb;
cb = undefined; cb = undefined;
@ -106,6 +111,60 @@ var readMessages = function (path, msgHandler, cb) {
stream.on('error', function (e) { complete(e); }); stream.on('error', function (e) { complete(e); });
}; };
const NEWLINE_CHR = ('\n').charCodeAt(0);
const mkBufferSplit = () => {
let remainder = null;
return Pull((read) => {
return (abort, cb) => {
read(abort, function (end, data) {
if (end) {
cb(end, remainder ? [remainder, data] : [data]);
remainder = null;
return;
}
const queue = [];
for (;;) {
const offset = data.indexOf(NEWLINE_CHR);
if (offset < 0) {
remainder = remainder ? Buffer.concat([remainder, data]) : data;
break;
}
let subArray = data.slice(0, offset);
if (remainder) {
subArray = Buffer.concat([remainder, subArray]);
remainder = null;
}
queue.push(subArray);
data = data.slice(offset + 1);
}
cb(end, queue);
});
};
}, Pull.flatten());
};
const mkOffsetCounter = () => {
let offset = 0;
return Pull.map((buff) => {
const out = { offset: offset, buff: buff };
// +1 for the eaten newline
offset += buff.length + 1;
return out;
});
};
const readMessagesBin = (env, id, start, msgHandler, cb) => {
const stream = Fs.createReadStream(mkPath(env, id), { start: start });
let keepReading = true;
Pull(
ToPull.read(stream),
mkBufferSplit(),
mkOffsetCounter(),
Pull.asyncMap((data, moreCb) => { msgHandler(data, moreCb, ()=>{ keepReading = false; moreCb(); }); }),
Pull.drain(()=>(keepReading), cb)
);
};
var checkPath = function (path, callback) { var checkPath = function (path, callback) {
// TODO check if we actually need to use stat at all // TODO check if we actually need to use stat at all
Fs.stat(path, function (err) { Fs.stat(path, function (err) {
@ -117,7 +176,8 @@ var checkPath = function (path, callback) {
callback(err); callback(err);
return; return;
} }
Fs.mkdir(Path.dirname(path), function (err) { // 511 -> octal 777
Fs.mkdir(Path.dirname(path), 511, function (err) {
if (err && err.code !== 'EEXIST') { if (err && err.code !== 'EEXIST') {
callback(err); callback(err);
return; return;
@ -154,7 +214,28 @@ var flushUnusedChannels = function (env, cb, frame) {
cb(); cb();
}; };
var getChannel = function (env, id, callback) { var channelBytes = function (env, chanName, cb) {
var path = mkPath(env, chanName);
Fs.stat(path, function (err, stats) {
if (err) { return void cb(err); }
cb(undefined, stats.size);
});
};
/*::
export type ChainPadServer_ChannelInternal_t = {
atime: number,
writeStream: typeof(process.stdout),
whenLoaded: ?Array<(err:?Error, chan:?ChainPadServer_ChannelInternal_t)=>void>,
onError: Array<(?Error)=>void>,
path: string
};
*/
var getChannel = function (
env,
id,
callback /*:(err:?Error, chan:?ChainPadServer_ChannelInternal_t)=>void*/
) {
if (env.channels[id]) { if (env.channels[id]) {
var chan = env.channels[id]; var chan = env.channels[id];
chan.atime = +new Date(); chan.atime = +new Date();
@ -178,9 +259,9 @@ var getChannel = function (env, id, callback) {
}); });
} }
var path = mkPath(env, id); var path = mkPath(env, id);
var channel = env.channels[id] = { var channel /*:ChainPadServer_ChannelInternal_t*/ = env.channels[id] = {
atime: +new Date(), atime: +new Date(),
writeStream: undefined, writeStream: (undefined /*:any*/),
whenLoaded: [ callback ], whenLoaded: [ callback ],
onError: [ ], onError: [ ],
path: path path: path
@ -193,6 +274,9 @@ var getChannel = function (env, id, callback) {
if (err) { if (err) {
delete env.channels[id]; delete env.channels[id];
} }
if (!channel.writeStream) {
throw new Error("getChannel() complete called without channel writeStream");
}
whenLoaded.forEach(function (wl) { wl(err, (err) ? undefined : channel); }); whenLoaded.forEach(function (wl) { wl(err, (err) ? undefined : channel); });
}; };
var fileExists; var fileExists;
@ -211,7 +295,7 @@ var getChannel = function (env, id, callback) {
var stream = channel.writeStream = Fs.createWriteStream(path, { flags: 'a' }); var stream = channel.writeStream = Fs.createWriteStream(path, { flags: 'a' });
env.openFiles++; env.openFiles++;
stream.on('open', waitFor()); stream.on('open', waitFor());
stream.on('error', function (err) { stream.on('error', function (err /*:?Error*/) {
env.openFiles--; env.openFiles--;
// this might be called after this nThen block closes. // this might be called after this nThen block closes.
if (channel.whenLoaded) { if (channel.whenLoaded) {
@ -228,20 +312,22 @@ var getChannel = function (env, id, callback) {
}); });
}; };
var message = function (env, chanName, msg, cb) { const messageBin = (env, chanName, msgBin, cb) => {
getChannel(env, chanName, function (err, chan) { getChannel(env, chanName, function (err, chan) {
if (err) { if (!chan) {
cb(err); cb(err);
return; return;
} }
let called = false;
var complete = function (err) { var complete = function (err) {
var _cb = cb; if (called) { return; }
cb = undefined; called = true;
if (_cb) { _cb(err); } cb(err);
}; };
chan.onError.push(complete); chan.onError.push(complete);
chan.writeStream.write(msg + '\n', function () { chan.writeStream.write(msgBin, function () {
chan.onError.splice(chan.onError.indexOf(complete) - 1, 1); /*::if (!chan) { throw new Error("Flow unreachable"); }*/
chan.onError.splice(chan.onError.indexOf(complete), 1);
if (!cb) { return; } if (!cb) { return; }
//chan.messages.push(msg); //chan.messages.push(msg);
chan.atime = +new Date(); chan.atime = +new Date();
@ -250,9 +336,13 @@ var message = function (env, chanName, msg, cb) {
}); });
}; };
var message = function (env, chanName, msg, cb) {
messageBin(env, chanName, new Buffer(msg + '\n', 'utf8'), cb);
};
var getMessages = function (env, chanName, handler, cb) { var getMessages = function (env, chanName, handler, cb) {
getChannel(env, chanName, function (err, chan) { getChannel(env, chanName, function (err, chan) {
if (err) { if (!chan) {
cb(err); cb(err);
return; return;
} }
@ -271,21 +361,43 @@ var getMessages = function (env, chanName, handler, cb) {
errorState = true; errorState = true;
return void cb(err); return void cb(err);
} }
if (!chan) { throw new Error("impossible, flow checking"); }
chan.atime = +new Date(); chan.atime = +new Date();
cb(); cb();
}); });
}); });
}; };
var channelBytes = function (env, chanName, cb) { /*::
var path = mkPath(env, chanName); export type ChainPadServer_MessageObj_t = { buff: Buffer, offset: number };
Fs.stat(path, function (err, stats) { export type ChainPadServer_Storage_t = {
if (err) { return void cb(err); } readMessagesBin: (
cb(void 0, stats.size); channelName:string,
}); start:number,
asyncMsgHandler:(msg:ChainPadServer_MessageObj_t, moreCb:()=>void, abortCb:()=>void)=>void,
cb:(err:?Error)=>void
)=>void,
message: (channelName:string, content:string, cb:(err:?Error)=>void)=>void,
messageBin: (channelName:string, content:Buffer, cb:(err:?Error)=>void)=>void,
getMessages: (channelName:string, msgHandler:(msg:string)=>void, cb:(err:?Error)=>void)=>void,
removeChannel: (channelName:string, cb:(err:?Error)=>void)=>void,
closeChannel: (channelName:string, cb:(err:?Error)=>void)=>void,
flushUnusedChannels: (cb:()=>void)=>void,
getChannelSize: (channelName:string, cb:(err:?Error, size:?number)=>void)=>void,
getChannelMetadata: (channelName:string, cb:(err:?Error|string, data:?any)=>void)=>void,
clearChannel: (channelName:string, (err:?Error)=>void)=>void
}; };
export type ChainPadServer_Config_t = {
module.exports.create = function (conf, cb) { verbose?: boolean,
filePath?: string,
channelExpirationMs?: number,
openFileLimit?: number
};
*/
module.exports.create = function (
conf /*:ChainPadServer_Config_t*/,
cb /*:(store:ChainPadServer_Storage_t)=>void*/
) {
var env = { var env = {
root: conf.filePath || './datastore', root: conf.filePath || './datastore',
channels: { }, channels: { },
@ -294,15 +406,22 @@ module.exports.create = function (conf, cb) {
openFiles: 0, openFiles: 0,
openFileLimit: conf.openFileLimit || 2048, openFileLimit: conf.openFileLimit || 2048,
}; };
Fs.mkdir(env.root, function (err) { // 0x1ff -> 777
Fs.mkdir(env.root, 0x1ff, function (err) {
if (err && err.code !== 'EEXIST') { if (err && err.code !== 'EEXIST') {
// TODO: somehow return a nice error // TODO: somehow return a nice error
throw err; throw err;
} }
cb({ cb({
readMessagesBin: (channelName, start, asyncMsgHandler, cb) => {
readMessagesBin(env, channelName, start, asyncMsgHandler, cb);
},
message: function (channelName, content, cb) { message: function (channelName, content, cb) {
message(env, channelName, content, cb); message(env, channelName, content, cb);
}, },
messageBin: (channelName, content, cb) => {
messageBin(env, channelName, content, cb);
},
getMessages: function (channelName, msgHandler, cb) { getMessages: function (channelName, msgHandler, cb) {
getMessages(env, channelName, msgHandler, cb); getMessages(env, channelName, msgHandler, cb);
}, },

@ -7,12 +7,12 @@ define([
CodeMirror.defineSimpleMode("orgmode", { CodeMirror.defineSimpleMode("orgmode", {
start: [ start: [
{regex: /^(^\*{1,6}\s)(TODO|DOING|WAITING|NEXT){0,1}(CANCELLED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED){0,1}(.*)$/, token: ["header org-level-star", "header org-todo", "header org-done", "header"]}, {regex: /^(^\*{1,6}\s)(TODO|DOING|WAITING|NEXT){0,1}(CANCELLED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED){0,1}(.*)$/, token: ["header org-level-star", "header org-todo", "header org-done", "header"]},
{regex: /(^\+[^\/]*\+)/, token: ["strikethrough"]}, {regex: /(\+[^\+]+\+)/, token: ["strikethrough"]},
{regex: /(^\*[^\/]*\*)/, token: ["strong"]}, {regex: /(\*[^\*]+\*)/, token: ["strong"]},
{regex: /(^\/[^\/]*\/)/, token: ["em"]}, {regex: /(\/[^\/]+\/)/, token: ["em"]},
{regex: /(^\_[^\/]*\_)/, token: ["link"]}, {regex: /(\_[^\_]+\_)/, token: ["link"]},
{regex: /(^\~[^\/]*\~)/, token: ["comment"]}, {regex: /(\~[^\~]+\~)/, token: ["comment"]},
{regex: /(^\=[^\/]*\=)/, token: ["comment"]}, {regex: /(\=[^\=]+\=)/, token: ["comment"]},
{regex: /\[\[[^\[\]]*\]\[[^\[\]]*\]\]/, token: "url"}, // links {regex: /\[\[[^\[\]]*\]\[[^\[\]]*\]\]/, token: "url"}, // links
{regex: /\[[xX\s]?\]/, token: 'qualifier'}, // checkbox {regex: /\[[xX\s]?\]/, token: 'qualifier'}, // checkbox
{regex: /\#\+BEGIN_[A-Z]*/, token: "comment", next: "env"}, // comments {regex: /\#\+BEGIN_[A-Z]*/, token: "comment", next: "env"}, // comments

@ -0,0 +1,115 @@
/*
* This is an internal configuration file.
* If you want to change some configurable values, use the '/customize/application_config.js'
* file (make a copy from /customize.dist/application_config.js)
*/
define(function() {
var config = {};
/* Select the buttons displayed on the main page to create new collaborative sessions
* Existing types : pad, code, poll, slide
*/
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file', 'todo', 'contacts'];
config.registeredOnlyTypes = ['file', 'contacts'];
/* Cryptpad apps use a common API to display notifications to users
* by default, notifications are hidden after 5 seconds
* You can change their duration here (measured in milliseconds)
*/
config.notificationTimeout = 5000;
config.disableUserlistNotifications = false;
config.hideLoadingScreenTips = false;
config.enablePinning = true;
// Update the default colors available in the whiteboard application
config.whiteboardPalette = [
'#000000', // black
'#FFFFFF', // white
'#848484', // grey
'#8B4513', // saddlebrown
'#FF0000', // red
'#FF8080', // peach?
'#FF8000', // orange
'#FFFF00', // yellow
'#80FF80', // light green
'#00FF00', // green
'#00FFFF', // cyan
'#008B8B', // dark cyan
'#0000FF', // blue
'#FF00FF', // fuschia
'#FF00C0', // hot pink
'#800080', // purple
];
// Set enableTemplates to false to remove the button allowing users to save a pad as a template
// and remove the template category in CryptDrive
config.enableTemplates = true;
// Set enableHistory to false to remove the "History" button in all the apps.
config.enableHistory = true;
/* user passwords are hashed with scrypt, and salted with their username.
this value will be appended to the username, causing the resulting hash
to differ from other CryptPad instances if customized. This makes it
such that anyone who wants to bruteforce common credentials must do so
again on each CryptPad instance that they wish to attack.
WARNING: this should only be set when your CryptPad instance is first
created. Changing it at a later time will break logins for all existing
users.
*/
config.loginSalt = '';
config.minimumPasswordLength = 8;
// Amount of time (ms) before aborting the session when the algorithm cannot synchronize the pad
config.badStateTimeout = 30000;
// Customize the icon used for each application.
// You can update the colors by making a copy of /customize.dist/src/less2/include/colortheme.less
config.applicationsIcon = {
file: 'fa-file-text-o',
pad: 'fa-file-word-o',
code: 'fa-file-code-o',
slide: 'fa-file-powerpoint-o',
poll: 'fa-calendar',
whiteboard: 'fa-paint-brush',
todo: 'fa-tasks',
contacts: 'fa-users',
};
// EXPERIMENTAL: Enabling "displayCreationScreen" may cause UI issues and possible loss of data
config.displayCreationScreen = false;
// Prevent anonymous users from storing pads in their drive
config.disableAnonymousStore = false;
// Hide the usage bar in settings and drive
//config.hideUsageBar = true;
// Disable feedback for all the users and hide the settings part about feedback
//config.disableFeedback = true;
// Add new options in the share modal (extend an existing tab or add a new tab).
// More info about how to use it on the wiki:
// https://github.com/xwiki-labs/cryptpad/wiki/Application-config#configcustomizeshareoptions
//config.customizeShareOptions = function (hashes, tabs, config) {};
// Add code to be executed on every page before loading the user object. `isLoggedIn` (bool) is
// indicating if the user is registered or anonymous. Here you can change the way anonymous users
// work in CryptPad, use an external SSO or even force registration
// *NOTE*: You have to call the `callback` function to continue the loading process
//config.beforeLogin = function(isLoggedIn, callback) {};
// Add code to be executed on every page after the user object is loaded (also work for
// unregistered users). This allows you to interact with your users' drive
// *NOTE*: You have to call the `callback` function to continue the loading process
//config.afterLogin = function(api, callback) {};
// Disabling the profile app allows you to import the profile informations (display name, avatar)
// from an external source and make sure the users can't change them from CryptPad.
// You can use config.afterLogin to import these values in the users' drive.
//config.disableProfile = true;
return config;
});

@ -1,4 +1,7 @@
define(['/customize/messages.js'], function (Messages) { define([
'/customize/messages.js',
'/customize/application_config.js'
], function (Messages, AppConfig) {
var Feedback = {}; var Feedback = {};
Feedback.init = function (state) { Feedback.init = function (state) {
@ -19,6 +22,7 @@ define(['/customize/messages.js'], function (Messages) {
http.send(); http.send();
}; };
Feedback.send = function (action, force) { Feedback.send = function (action, force) {
if (AppConfig.disableFeedback) { return; }
if (!action) { return; } if (!action) { return; }
if (force !== true) { if (force !== true) {
if (!Feedback.state) { return; } if (!Feedback.state) { return; }

@ -953,7 +953,32 @@ define([
}; };
if (!window.Symbol) { return void displayDefault(); } // IE doesn't have Symbol if (!window.Symbol) { return void displayDefault(); } // IE doesn't have Symbol
if (!href) { return void displayDefault(); } if (!href) { return void displayDefault(); }
var centerImage = function ($img, $image, img) {
var w = img.width;
var h = img.height;
if (w>h) {
$image.css('max-height', '100%');
$img.css('flex-direction', 'column');
if (cb) { cb($img); }
return;
}
$image.css('max-width', '100%');
$img.css('flex-direction', 'row');
if (cb) { cb($img); }
};
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
if (parsed.type !== "file" || parsed.hashData.type !== "file") {
var $img = $('<media-tag>').appendTo($container);
var img = new Image();
$(img).attr('src', href);
img.onload = function () {
centerImage($img, $(img), img);
$(img).appendTo($img);
};
return;
}
var secret = Hash.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) { if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
@ -971,17 +996,7 @@ define([
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey); $img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
UIElements.displayMediatagImage(Common, $img, function (err, $image, img) { UIElements.displayMediatagImage(Common, $img, function (err, $image, img) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
var w = img.width; centerImage($img, $image, img);
var h = img.height;
if (w>h) {
$image.css('max-height', '100%');
$img.css('flex-direction', 'column');
if (cb) { cb($img); }
return;
}
$image.css('max-width', '100%');
$img.css('flex-direction', 'row');
if (cb) { cb($img); }
}); });
}); });
} }
@ -1259,7 +1274,7 @@ define([
$userAdminContent.append($userAccount).append(Util.fixHTML(accountName)); $userAdminContent.append($userAccount).append(Util.fixHTML(accountName));
$userAdminContent.append($('<br>')); $userAdminContent.append($('<br>'));
} }
if (config.displayName) { if (config.displayName && !AppConfig.disableProfile) {
// Hide "Display name:" in read only mode // Hide "Display name:" in read only mode
$userName.append(Messages.user_displayName + ': '); $userName.append(Messages.user_displayName + ': ');
$userName.append($displayedName); $userName.append($displayedName);
@ -1282,14 +1297,14 @@ define([
}); });
} }
// Add the change display name button if not in read only mode // Add the change display name button if not in read only mode
if (config.changeNameButtonCls && config.displayChangeName) { if (config.changeNameButtonCls && config.displayChangeName && !AppConfig.disableProfile) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': config.changeNameButtonCls}, attributes: {'class': config.changeNameButtonCls},
content: Messages.user_rename content: Messages.user_rename
}); });
} }
if (accountName) { if (accountName && !AppConfig.disableProfile) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {'class': 'cp-toolbar-menu-profile'}, attributes: {'class': 'cp-toolbar-menu-profile'},

@ -727,6 +727,10 @@ define([
}; };
Nthen(function (waitFor) { Nthen(function (waitFor) {
if (AppConfig.beforeLogin) {
AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor());
}
}).nThen(function (waitFor) {
var cfg = { var cfg = {
query: onMessage, // TODO temporary, will be replaced by a webworker channel query: onMessage, // TODO temporary, will be replaced by a webworker channel
userHash: LocalStore.getUserHash(), userHash: LocalStore.getUserHash(),
@ -763,6 +767,7 @@ define([
} }
initFeedback(data.feedback); initFeedback(data.feedback);
initialized = true;
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Load the new pad when the hash has changed // Load the new pad when the hash has changed
@ -829,6 +834,10 @@ define([
delete sessionStorage.migrateAnonDrive; delete sessionStorage.migrateAnonDrive;
})); }));
} }
}).nThen(function (waitFor) {
if (AppConfig.afterLogin) {
AppConfig.afterLogin(common, waitFor());
}
}).nThen(function () { }).nThen(function () {
updateLocalVersion(); updateLocalVersion();
f(void 0, env); f(void 0, env);

@ -341,7 +341,7 @@ define([
// "priv" is not shared with other users but is needed by the apps // "priv" is not shared with other users but is needed by the apps
priv: { priv: {
edPublic: store.proxy.edPublic, edPublic: store.proxy.edPublic,
friends: store.proxy.friends, friends: store.proxy.friends || {},
settings: store.proxy.settings, settings: store.proxy.settings,
thumbnails: !Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']) thumbnails: !Util.find(store.proxy, ['settings', 'general', 'disableThumbnails'])
} }

@ -238,6 +238,7 @@ define([
var $nameValue = $('<span>', { var $nameValue = $('<span>', {
'class': 'cp-toolbar-userlist-name-value' 'class': 'cp-toolbar-userlist-name-value'
}).text(name).appendTo($nameSpan); }).text(name).appendTo($nameSpan);
if (!Config.disableProfile) {
var $button = $('<button>', { var $button = $('<button>', {
'class': 'fa fa-pencil cp-toolbar-userlist-name-edit', 'class': 'fa fa-pencil cp-toolbar-userlist-name-edit',
title: Messages.user_rename title: Messages.user_rename
@ -288,6 +289,7 @@ define([
editingUserName.select[1]); editingUserName.select[1]);
setTimeout(function () { $nameInput.focus(); }); setTimeout(function () { $nameInput.focus(); });
} }
}
} else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic] } else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic]
&& !priv.readOnly) { && !priv.readOnly) {
if (pendingFriends.indexOf(data.netfluxId) !== -1) { if (pendingFriends.indexOf(data.netfluxId) !== -1) {

@ -571,7 +571,6 @@ define([
// RENAME // RENAME
exp.rename = function (path, newName, cb) { exp.rename = function (path, newName, cb) {
if (sframeChan) { if (sframeChan) {
console.log(path, newName);
return void sframeChan.query("Q_DRIVE_USEROBJECT", { return void sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "rename", cmd: "rename",
data: { data: {

@ -18,50 +18,6 @@
<div id="cp-app-drive-toolbar"></div> <div id="cp-app-drive-toolbar"></div>
<div id="cp-app-drive-content" tabindex="2"></div> <div id="cp-app-drive-content" tabindex="2"></div>
</div> </div>
<div id="cp-app-drive-context-tree" class="cp-app-drive-context dropdown cp-unselectable">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" data-icon="fa-folder-open" class="cp-app-drive-context-open dropdown-item" data-localization="fc_open">Open</a></li>
<li><a tabindex="-1" data-icon="fa-eye" class="cp-app-drive-context-openro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
<li><a tabindex="-1" data-icon="fa-pencil" class="cp-app-drive-context-rename cp-app-drive-context-editable dropdown-item" data-localization="fc_rename">Rename</a></li>
<li><a tabindex="-1" data-icon="fa-trash" class="cp-app-drive-context-delete cp-app-drive-context-editable dropdown-item" data-localization="fc_delete">Delete</a></li>
<li><a tabindex="-1" data-icon="fa-eraser" class="cp-app-drive-context-deleteowned dropdown-item" data-localization="fc_delete_owned">Delete permanently</a></li>
<li><a tabindex="-1" data-icon="fa-folder" class="cp-app-drive-context-newfolder cp-app-drive-context-editable dropdown-item" data-localization="fc_newfolder">New folder</a></li>
<li><a tabindex="-1" data-icon="fa-database" class="cp-app-drive-context-properties dropdown-item" data-localization="fc_prop">Properties</a></li>
<li><a tabindex="-1" data-icon="fa-hashtag" class="cp-app-drive-context-hashtag dropdown-item" data-localization="fc_hashtag">Tags</a></li>
</ul>
</div>
<div id="cp-app-drive-context-content" class="cp-app-drive-context dropdown cp-unselectable">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" data-icon="fa-folder" class="cp-app-drive-context-newfolder cp-app-drive-context-editable dropdown-item" data-localization="fc_newfolder">New folder</a></li>
<li><a tabindex="-1" data-icon="fa-file-word-o" class="cp-app-drive-context-newdoc cp-app-drive-context-own cp-app-drive-context-editable dropdown-item" data-type="pad" data-localization="button_newpad">New pad</a></li>
<li><a tabindex="-1" data-icon="fa-file-code-o" class="cp-app-drive-context-newdoc cp-app-drive-context-own cp-app-drive-context-editable dropdown-item" data-type="code" data-localization="button_newcode">New code</a></li>
<li><a tabindex="-1" data-icon="fa-file-powerpoint-o" class="cp-app-drive-context-newdoc cp-app-drive-context-own cp-app-drive-context-editable dropdown-item" data-type="slide" data-localization="button_newslide">New slide</a></li>
<li><a tabindex="-1" data-icon="fa-calendar" class="cp-app-drive-context-newdoc cp-app-drive-context-own cp-app-drive-context-editable dropdown-item" data-type="poll" data-localization="button_newpoll">New poll</a></li>
<li><a tabindex="-1" data-icon="fa-paint-brush" class="cp-app-drive-context-newdoc cp-app-drive-context-own cp-app-drive-context-editable dropdown-item" data-type="whiteboard" data-localization="button_newwhiteboard">New whiteboard</a></li>
</ul>
</div>
<div id="cp-app-drive-context-default" class="cp-app-drive-context dropdown cp-unselectable">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" data-icon="fa-folder-open" class="cp-app-drive-context-open dropdown-item" data-localization="fc_open">Open</a></li>
<li><a tabindex="-1" data-icon="fa-eye" class="cp-app-drive-context-openro dropdown-item" data-localization="fc_open_ro">Open (read-only)</a></li>
<li><a tabindex="-1" data-icon="fa-trash" class="cp-app-drive-context-delete dropdown-item" data-localization="fc_delete">Delete</a></li>
<li><a tabindex="-1" data-icon="fa-eraser" class="cp-app-drive-context-deleteowned dropdown-item" data-localization="fc_delete_owned">Delete permanently</a></li>
<li><a tabindex="-1" data-icon="fa-database" class="cp-app-drive-context-properties dropdown-item" data-localization="fc_prop">Properties</a></li>
<li><a tabindex="-1" data-icon="fa-hashtag" class="cp-app-drive-context-hashtag dropdown-item" data-localization="fc_hashtag">Tags</a></li>
</ul>
</div>
<div id="cp-app-drive-context-trashtree" class="cp-app-drive-context dropdown cp-unselectable">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" data-icon="fa-trash-o" class="cp-app-drive-context-empty cp-app-drive-context-editable dropdown-item" data-localization="fc_empty">Empty the trash</a></li>
</ul>
</div>
<div id="cp-app-drive-context-trash" class="cp-app-drive-context dropdown cp-unselectable">
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu" style="display:block;position:static;margin-bottom:5px;">
<li><a tabindex="-1" data-icon="fa-eraser" class="cp-app-drive-context-remove cp-app-drive-context-editable dropdown-item" data-localization="fc_remove">Delete permanently</a></li>
<li><a tabindex="-1" data-icon="fa-repeat" class="cp-app-drive-context-restore cp-app-drive-context-editable dropdown-item" data-localization="fc_restore">Restore</a></li>
<li><a tabindex="-1" data-icon="fa-database" class="cp-app-drive-context-properties dropdown-item" data-localization="fc_prop">Properties</a></li>
</ul>
</div>
</div> </div>
</body> </body>
</html> </html>

@ -11,6 +11,7 @@ define([
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/hyperscript.js',
'/common/userObject.js', '/common/userObject.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-listmap/chainpad-listmap.js',
@ -32,6 +33,7 @@ define([
nThen, nThen,
SFCommon, SFCommon,
CommonRealtime, CommonRealtime,
h,
FO, FO,
AppConfig, AppConfig,
Listmap, Listmap,
@ -155,19 +157,30 @@ define([
}; };
// Icons // Icons
var faFolder = 'fa-folder';
var faFolderOpen = 'fa-folder-open';
var faReadOnly = 'fa-eye';
var faRename = 'fa-pencil';
var faTrash = 'fa-trash';
var faDelete = 'fa-eraser';
var faProperties = 'fa-database';
var faTags = 'fa-hashtag';
var faEmpty = 'fa-trash-o';
var faRestore = 'fa-repeat';
var faShowParent = 'fa-location-arrow';
var $folderIcon = $('<span>', { var $folderIcon = $('<span>', {
"class": "fa fa-folder cp-app-drive-icon-folder cp-app-drive-content-icon" "class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
}); });
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"}); //var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
var $folderEmptyIcon = $folderIcon.clone(); var $folderEmptyIcon = $folderIcon.clone();
var $folderOpenedIcon = $('<span>', {"class": "fa fa-folder-open cp-app-drive-icon-folder"}); var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"}); //var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone(); var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"}); //var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"}); var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
var $templateIcon = $('<span>', {"class": "fa fa-cubes"}); var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"}); var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
var $trashIcon = $('<span>', {"class": "fa fa-trash"}); var $trashIcon = $('<span>', {"class": "fa " + faTrash});
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"}); var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"}); //var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"}); var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
@ -181,7 +194,7 @@ define([
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"}); var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
var $addIcon = $('<span>', {"class": "fa fa-plus"}); var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"}); var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa fa-eye"}); var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"}); var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"}); var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
@ -200,6 +213,91 @@ define([
}); });
}; };
var createContextMenu = function () {
var menu = h('div.cp-app-drive-context.dropdown.cp-unselectable', [
h('ul.dropdown-menu', {
'role': 'menu',
'aria-labelledby': 'dropdownMenu',
'style': 'display:block;position:static;margin-bottom:5px;'
}, [
h('li', h('a.cp-app-drive-context-open.dropdown-item', {
'tabindex': '-1',
'data-icon': faFolderOpen,
}, Messages.fc_open)),
h('li', h('a.cp-app-drive-context-openro.dropdown-item', {
'tabindex': '-1',
'data-icon': faReadOnly,
}, Messages.fc_open_ro)),
h('li', h('a.cp-app-drive-context-openparent.dropdown-item', {
'tabindex': '-1',
'data-icon': faShowParent,
}, Messages.fm_openParent)),
h('li', h('a.cp-app-drive-context-newfolder.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faFolder,
}, Messages.fc_newfolder)),
h('li', h('a.cp-app-drive-context-hashtag.dropdown-item', {
'tabindex': '-1',
'data-icon': faTags,
}, Messages.fc_hashtag)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable.cp-app-drive-context-own', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.pad,
'data-type': 'pad'
}, Messages.button_newpad)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable.cp-app-drive-context-own', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.code,
'data-type': 'code'
}, Messages.button_newcode)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable.cp-app-drive-context-own', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.slide,
'data-type': 'slide'
}, Messages.button_newslide)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable.cp-app-drive-context-own', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.poll,
'data-type': 'poll'
}, Messages.button_newpoll)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable.cp-app-drive-context-own', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.whiteboard,
'data-type': 'whiteboard'
}, Messages.button_newwhiteboard)),
h('li', h('a.cp-app-drive-context-empty.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faEmpty,
}, Messages.fc_empty)),
h('li', h('a.cp-app-drive-context-restore.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faRestore,
}, Messages.fc_restore)),
h('li', h('a.cp-app-drive-context-rename.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faRename,
}, Messages.fc_rename)),
h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faTrash,
}, Messages.fc_delete)),
h('li', h('a.cp-app-drive-context-deleteowned.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faDelete,
}, Messages.fc_delete_owned)),
h('li', h('a.cp-app-drive-context-remove.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': faDelete,
}, Messages.fc_remove)),
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
'tabindex': '-1',
'data-icon': faProperties,
}, Messages.fc_prop)),
])
]);
return $(menu);
};
var andThen = function (common, proxy) { var andThen = function (common, proxy) {
var files = proxy.drive; var files = proxy.drive;
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
@ -227,7 +325,8 @@ define([
var $content = APP.$content = $("#cp-app-drive-content"); var $content = APP.$content = $("#cp-app-drive-content");
var $appContainer = $(".cp-app-drive-container"); var $appContainer = $(".cp-app-drive-container");
var $driveToolbar = $("#cp-app-drive-toolbar"); var $driveToolbar = $("#cp-app-drive-toolbar");
var $contextMenu = $("#cp-app-drive-context-tree"); var $contextMenu = createContextMenu().appendTo($appContainer);
var $contentContextMenu = $("#cp-app-drive-context-content"); var $contentContextMenu = $("#cp-app-drive-context-content");
var $defaultContextMenu = $("#cp-app-drive-context-default"); var $defaultContextMenu = $("#cp-app-drive-context-default");
var $trashTreeContextMenu = $("#cp-app-drive-context-trashtree"); var $trashTreeContextMenu = $("#cp-app-drive-context-trashtree");
@ -638,68 +737,112 @@ define([
},0); },0);
}; };
var filterContextMenu = function ($menu, paths) { var filterContextMenu = function (type, paths) {
//var path = $element.data('path');
if (!paths || paths.length === 0) { logError('no paths'); } if (!paths || paths.length === 0) { logError('no paths'); }
$contextMenu.find('li').hide();
var show = [];
var filter;
if (type === "content") {
filter = function ($el, className) {
if (className === 'newfolder') { return; }
return AppConfig.availablePadTypes.indexOf($el.attr('data-type')) === -1;
};
} else {
// In case of multiple selection, we must hide the option if at least one element
// is not compatible
var containsFolder = false;
var hide = []; var hide = [];
var hasFolder = false;
paths.forEach(function (p) { paths.forEach(function (p) {
var path = p.path; var path = p.path;
var $element = p.element; var $element = p.element;
if (path.length === 1) { if (path.length === 1) {
// Can't rename or delete root elements // Can't rename or delete root elements
hide.push($menu.find('a.cp-app-drive-context-rename')); hide.push('delete');
hide.push($menu.find('a.cp-app-drive-context-delete')); hide.push('rename');
}
if (!APP.editable) {
hide.push($menu.find('a.cp-app-drive-context-editable'));
}
if (!isOwnDrive()) {
hide.push($menu.find('a.cp-app-drive-context-own'));
} }
if (!$element.is('.cp-app-drive-element-owned')) { if (!$element.is('.cp-app-drive-element-owned')) {
hide.push($menu.find('a.cp-app-drive-context-deleteowned')); hide.push('deleteowned');
} }
if ($element.is('.cp-app-drive-element-notrash')) { if ($element.is('.cp-app-drive-element-notrash')) {
hide.push($menu.find('a.cp-app-drive-context-delete')); // We can't delete elements in virtual categories
hide.push('delete');
} else {
// We can only open parent in virtual categories
hide.push('openparent');
} }
if ($element.is('.cp-app-drive-element-file')) { if ($element.is('.cp-app-drive-element-file')) {
// No folder in files // No folder in files
hide.push($menu.find('a.cp-app-drive-context-newfolder')); hide.push('newfolder');
if ($element.is('.cp-app-drive-element-readonly')) { if ($element.is('.cp-app-drive-element-readonly')) {
// Keep only open readonly hide.push('open'); // Remove open 'edit' mode
hide.push($menu.find('a.cp-app-drive-context-open'));
} else if ($element.is('.cp-app-drive-element-noreadonly')) { } else if ($element.is('.cp-app-drive-element-noreadonly')) {
// Keep only open readonly hide.push('openro'); // Remove open 'view' mode
hide.push($menu.find('a.cp-app-drive-context-openro'));
} }
} else { } else { // it's a folder
if (hasFolder) { if (containsFolder) {
// More than 1 folder selected: cannot create a new subfolder // More than 1 folder selected: cannot create a new subfolder
hide.push($menu.find('a.cp-app-drive-context-newfolder')); hide.push('newfolder');
} }
hasFolder = true; containsFolder = true;
hide.push($menu.find('a.cp-app-drive-context-openro')); hide.push('openro');
hide.push($menu.find('a.cp-app-drive-context-properties')); hide.push('properties');
hide.push($menu.find('a.cp-app-drive-context-hashtag')); hide.push('hashtag');
} }
// If we're in the trash, hide restore and properties for non-root elements // If we're in the trash, hide restore and properties for non-root elements
if ($menu.find('a.cp-app-drive-context-restore').length && path && path.length > 4) { if (type === "trash" && path && path.length > 4) {
hide.push($menu.find('a.cp-app-drive-context-restore')); hide.push('restore');
hide.push($menu.find('a.cp-app-drive-context-properties')); hide.push('properties');
} }
}); });
if (paths.length > 1) { if (paths.length > 1) {
hide.push($menu.find('a.cp-app-drive-context-restore')); hide.push('restore');
hide.push($menu.find('a.cp-app-drive-context-properties')); hide.push('properties');
hide.push($menu.find('a.cp-app-drive-context-rename')); hide.push('rename');
hide.push('openparent');
} }
if (hasFolder && paths.length > 1) { if (containsFolder && paths.length > 1) {
// Cannot open multiple folders // Cannot open multiple folders
hide.push($menu.find('a.cp-app-drive-context-open')); hide.push('open');
} }
return hide;
filter = function ($el, className) {
if (hide.indexOf(className) !== -1) { return true; }
};
}
switch(type) {
case 'content':
show = ['newfolder', 'newdoc'];
break;
case 'tree':
show = ['open', 'openro', 'rename', 'delete', 'deleteowned', 'newfolder',
'properties', 'hashtag'];
break;
case 'default':
show = ['open', 'openro', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag'];
break;
case 'trashtree': {
show = ['empty'];
break;
}
case 'trash': {
show = ['remove', 'restore', 'properties'];
}
}
var filtered = [];
show.forEach(function (className) {
var $el = $contextMenu.find('.cp-app-drive-context-' + className);
if (!APP.editable && $el.is('.cp-app-drive-context-editable')) { return; }
if (!isOwnDrive && $el.is('.cp-app-drive-context-own')) { return; }
if (filter($el, className)) { return; }
$el.parent('li').show();
filtered.push('.cp-app-drive-context-' + className);
});
return filtered;
}; };
var getSelectedPaths = function ($element) { var getSelectedPaths = function ($element) {
@ -763,16 +906,13 @@ define([
$container.html(''); $container.html('');
var $element = $li.length === 1 ? $li : $($li[0]); var $element = $li.length === 1 ? $li : $($li[0]);
var paths = getSelectedPaths($element); var paths = getSelectedPaths($element);
var $menu = $element.data('context'); var menuType = $element.data('context');
if (!$menu) { return; } if (!menuType) { return; }
//var actions = []; //var actions = [];
var $actions = $menu.find('a'); var toShow = filterContextMenu(menuType, paths);
var toHide = filterContextMenu($menu, paths); var $actions = $contextMenu.find('a');
$actions = $actions.filter(function (i, el) { $actions = $actions.filter(function (i, el) {
for (var j = 0; j < toHide.length; j++) { return toShow.some(function (className) { return $(el).is(className); });
if ($(el).is(toHide[j])) { return false; }
}
return true;
}); });
$actions.each(function (i, el) { $actions.each(function (i, el) {
var $a = $('<button>', {'class': 'cp-app-drive-element'}); var $a = $('<button>', {'class': 'cp-app-drive-element'});
@ -863,7 +1003,8 @@ define([
updateContextButton(); updateContextButton();
}; };
var displayMenu = function (e, $menu) { var displayMenu = function (e) {
var $menu = $contextMenu;
$menu.css({ display: "block" }); $menu.css({ display: "block" });
if (APP.mobile()) { return; } if (APP.mobile()) { return; }
var h = $menu.outerHeight(); var h = $menu.outerHeight();
@ -905,102 +1046,50 @@ define([
}; };
// Open the selected context menu on the closest "li" element // Open the selected context menu on the closest "li" element
var openContextMenu = function (e, $menu) { var openContextMenu = function (type) {
return function (e) {
APP.hideMenu(); APP.hideMenu();
e.stopPropagation(); e.stopPropagation();
var paths;
if (type === 'content') {
paths = [{path: $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path')}];
if (!paths) { return; }
removeSelected();
} else {
var $element = findDataHolder($(e.target)); var $element = findDataHolder($(e.target));
if (type === 'trash' && !$element.data('path')) { return; }
if (!$element.length) { if (!$element.length) {
logError("Unable to locate the .element tag", e.target); logError("Unable to locate the .element tag", e.target);
$menu.hide();
log(Messages.fm_contextMenuError); log(Messages.fm_contextMenuError);
return false; return false;
} }
if (!$element.hasClass('cp-app-drive-element-selected')) { //paths.length === 1) { if (!$element.hasClass('cp-app-drive-element-selected')) {
onElementClick(undefined, $element); onElementClick(undefined, $element);
} }
var paths = getSelectedPaths($element); paths = getSelectedPaths($element);
var toHide = filterContextMenu($menu, paths);
toHide.forEach(function ($a) {
$a.parent('li').hide();
});
displayMenu(e, $menu);
if ($menu.find('li:visible').length === 0) {
debug("No visible element in the context menu. Abort.");
$menu.hide();
return true;
} }
$menu.find('a').data('paths', paths); $contextMenu.attr('data-menu-type', type);
//$menu.find('a').data('path', path);
//$menu.find('a').data('element', $element);
return false;
};
var openDirectoryContextMenu = function (e) {
$contextMenu.find('li').show();
openContextMenu(e, $contextMenu);
return false;
};
var openDefaultContextMenu = function (e) {
$defaultContextMenu.find('li').show();
openContextMenu(e, $defaultContextMenu);
return false;
};
var openTrashTreeContextMenu = function (e) {
removeSelected();
$trashTreeContextMenu.find('li').show();
openContextMenu(e, $trashTreeContextMenu);
return false;
};
var openTrashContextMenu = function (e) {
var path = findDataHolder($(e.target)).data('path');
if (!path) { return; }
$trashContextMenu.find('li').show();
openContextMenu(e, $trashContextMenu);
return false;
};
var openContentContextMenu = function (e) { filterContextMenu(type, paths);
APP.hideMenu();
e.stopPropagation();
var path = $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path');
if (!path) { return; }
var $menu = $contentContextMenu;
removeSelected();
if (!APP.editable) { displayMenu(e);
$menu.find('a.cp-app-drive-context-editable').parent('li').hide();
}
if (!isOwnDrive()) {
$menu.find('a.cp-app-drive-context-own').parent('li').hide();
}
$menu.find('[data-type]').each(function (idx, el) { if ($contextMenu.find('li:visible').length === 0) {
if (AppConfig.availablePadTypes.indexOf($(el).attr('data-type')) === -1) {
$(el).hide();
}
});
displayMenu(e, $menu);
if ($menu.find('li:visible').length === 0) {
debug("No visible element in the context menu. Abort."); debug("No visible element in the context menu. Abort.");
$menu.hide(); $contextMenu.hide();
return true; return true;
} }
$menu.find('a').data('path', path); $contextMenu.data('paths', paths);
return false; return false;
}; };
};
var getElementName = function (path) { var getElementName = function (path) {
var file = filesOp.find(path); var file = filesOp.find(path);
@ -1303,11 +1392,11 @@ define([
onElementClick(e, $element, newPath); onElementClick(e, $element, newPath);
}); });
if (!isTrash) { if (!isTrash) {
$element.contextmenu(openDirectoryContextMenu); $element.contextmenu(openContextMenu('tree'));
$element.data('context', $contextMenu); $element.data('context', 'tree');
} else { } else {
$element.contextmenu(openTrashContextMenu); $element.contextmenu(openContextMenu('trash'));
$element.data('context', $trashContextMenu); $element.data('context', 'trash');
} }
var isNewFolder = APP.newFolder && filesOp.comparePath(newPath, APP.newFolder); var isNewFolder = APP.newFolder && filesOp.comparePath(newPath, APP.newFolder);
if (isNewFolder) { if (isNewFolder) {
@ -1911,8 +2000,8 @@ define([
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element, path); onElementClick(e, $element, path);
}); });
$element.contextmenu(openDefaultContextMenu); $element.contextmenu(openContextMenu('default'));
$element.data('context', $defaultContextMenu); $element.data('context', 'default');
if (draggable) { if (draggable) {
addDragAndDropHandlers($element, path, false, false); addDragAndDropHandlers($element, path, false, false);
} }
@ -1951,8 +2040,8 @@ define([
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element); onElementClick(e, $element);
}); });
$element.contextmenu(openDefaultContextMenu); $element.contextmenu(openContextMenu('default'));
$element.data('context', $defaultContextMenu); $element.data('context', 'default');
$container.append($element); $container.append($element);
}); });
}; };
@ -2082,8 +2171,8 @@ define([
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element, path); onElementClick(e, $element, path);
}); });
$element.contextmenu(openDefaultContextMenu); $element.contextmenu(openContextMenu('default'));
$element.data('context', $defaultContextMenu); $element.data('context', 'default');
/*if (draggable) { /*if (draggable) {
addDragAndDropHandlers($element, path, false, false); addDragAndDropHandlers($element, path, false, false);
}*/ }*/
@ -2122,8 +2211,8 @@ define([
e.stopPropagation(); e.stopPropagation();
onElementClick(e, $element); onElementClick(e, $element);
}); });
$element.contextmenu(openDefaultContextMenu); $element.contextmenu(openContextMenu('default'));
$element.data('context', $defaultContextMenu); $element.data('context', 'default');
$container.append($element); $container.append($element);
}); });
}; };
@ -2261,7 +2350,7 @@ define([
} else if (isOwned) { } else if (isOwned) {
displayOwned($list); displayOwned($list);
} else { } else {
$dirContent.contextmenu(openContentContextMenu); $dirContent.contextmenu(openContextMenu('content'));
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); } if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
// display sub directories // display sub directories
var keys = Object.keys(root); var keys = Object.keys(root);
@ -2374,7 +2463,7 @@ define([
} }
$rootElement.addClass('cp-app-drive-tree-root'); $rootElement.addClass('cp-app-drive-tree-root');
$rootElement.find('>.cp-app-drive-element-row') $rootElement.find('>.cp-app-drive-element-row')
.contextmenu(openDirectoryContextMenu); .contextmenu(openContextMenu('tree'));
$('<ul>', {'class': 'cp-app-drive-tree-docs'}) $('<ul>', {'class': 'cp-app-drive-tree-docs'})
.append($rootElement).appendTo($container); .append($rootElement).appendTo($container);
$container = $rootElement; $container = $rootElement;
@ -2396,7 +2485,7 @@ define([
(isCurrentFolder ? $folderOpenedIcon : $folderIcon); (isCurrentFolder ? $folderOpenedIcon : $folderIcon);
var $element = createTreeElement(key, $icon.clone(), newPath, true, true, subfolder, isCurrentFolder); var $element = createTreeElement(key, $icon.clone(), newPath, true, true, subfolder, isCurrentFolder);
$element.appendTo($list); $element.appendTo($list);
$element.find('>.cp-app-drive-element-row').contextmenu(openDirectoryContextMenu); $element.find('>.cp-app-drive-element-row').contextmenu(openContextMenu('tree'));
createTree($element, newPath); createTree($element, newPath);
}); });
}; };
@ -2425,7 +2514,8 @@ define([
var isOpened = filesOp.comparePath(path, currentPath); var isOpened = filesOp.comparePath(path, currentPath);
var $trashElement = createTreeElement(TRASH_NAME, $icon, [TRASH], false, true, false, isOpened); var $trashElement = createTreeElement(TRASH_NAME, $icon, [TRASH], false, true, false, isOpened);
$trashElement.addClass('root'); $trashElement.addClass('root');
$trashElement.find('>.cp-app-drive-element-row').contextmenu(openTrashTreeContextMenu); $trashElement.find('>.cp-app-drive-element-row')
.contextmenu(openContextMenu('trashtree'));
var $trashList = $('<ul>', { 'class': 'cp-app-drive-tree-category' }) var $trashList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
.append($trashElement); .append($trashElement);
$container.append($trashList); $container.append($trashList);
@ -2588,22 +2678,44 @@ define([
UIElements.getProperties(common, data, cb); UIElements.getProperties(common, data, cb);
}; };
if (!APP.loggedIn) {
$contextMenu.find('.cp-app-drive-context-delete').text(Messages.fc_remove)
.attr('data-icon', 'fa-eraser');
}
var deletePaths = function (paths) {
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
filesOp.delete(pathsList, refresh);
});
};
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {
e.stopPropagation(); e.stopPropagation();
var paths = $(this).data('paths'); var paths = $contextMenu.data('paths');
var pathsList = [];
var type = $contextMenu.attr('data-menu-type');
var el; var el;
if (paths.length === 0) { if (paths.length === 0) {
log(Messages.fm_forbidden); log(Messages.fm_forbidden);
debug("Directory context menu on a forbidden or unexisting element. ", paths); debug("Context menu on a forbidden or unexisting element. ", paths);
return; return;
} }
if ($(this).hasClass("cp-app-drive-context-rename")) { if ($(this).hasClass("cp-app-drive-context-rename")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
displayRenameInput(paths[0].element, paths[0].path); displayRenameInput(paths[0].element, paths[0].path);
} }
else if($(this).hasClass("cp-app-drive-context-delete")) { else if($(this).hasClass("cp-app-drive-context-delete")) {
var pathsList = []; if (!APP.loggedIn) {
return void deletePaths(paths);
}
paths.forEach(function (p) { pathsList.push(p.path); }); paths.forEach(function (p) { pathsList.push(p.path); });
moveElements(pathsList, [TRASH], false, refresh); moveElements(pathsList, [TRASH], false, refresh);
} }
@ -2640,108 +2752,37 @@ define([
else if ($(this).hasClass('cp-app-drive-context-openro')) { else if ($(this).hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) { paths.forEach(function (p) {
var el = filesOp.find(p.path); var el = filesOp.find(p.path);
if (filesOp.isFolder(el)) { return; } if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; }
if (!el || filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el); var roUrl = getReadOnlyUrl(el);
openFile(null, roUrl); openFile(null, roUrl);
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-newfolder')) { else if ($(this).hasClass('cp-app-drive-context-newfolder')) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
var onCreated = function (err, info) { var onFolderCreated = function (err, info) {
if (err) { return void logError(err); } if (err) { return void logError(err); }
APP.newFolder = info.newPath; APP.newFolder = info.newPath;
APP.displayDirectory(paths[0].path); APP.displayDirectory(paths[0].path);
}; };
filesOp.addFolder(paths[0].path, null, onCreated); filesOp.addFolder(paths[0].path, null, onFolderCreated);
}
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1) { return; }
el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) {
if (e) { return void logError(e); }
UI.alert($prop[0], undefined, true);
});
}
else if ($(this).hasClass("cp-app-drive-context-hashtag")) {
if (paths.length !== 1) { return; }
el = filesOp.find(paths[0].path);
var data = filesOp.getFileData(el);
if (!data) { return void console.error("Expected to find a file"); }
var href = data.href;
common.updateTags(href);
}
APP.hideMenu();
});
if (!APP.loggedIn) {
$defaultContextMenu.find('.cp-app-drive-context-delete').text(Messages.fc_remove)
.attr('data-icon', 'fa-eraser');
}
$defaultContextMenu.on("click", "a", function(e) {
e.stopPropagation();
var paths = $(this).data('paths');
var el;
if (paths.length === 0) {
log(Messages.fm_forbidden);
debug("Context menu on a forbidden or unexisting element. ", paths);
return;
}
if ($(this).hasClass('cp-app-drive-context-open')) {
paths.forEach(function (p) {
var $element = p.element;
$element.dblclick();
});
} }
else if ($(this).hasClass('cp-app-drive-context-openro')) { else if ($(this).hasClass("cp-app-drive-context-newdoc")) {
paths.forEach(function (p) { var ntype = $(this).data('type') || 'pad';
var el = filesOp.find(p.path); var path2 = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; } common.sessionStorage.put(Constants.newPadPathKey, path2, function () {
if (!el || filesOp.isFolder(el)) { return; } common.openURL('/' + ntype + '/');
var roUrl = getReadOnlyUrl(el);
openFile(null, roUrl);
});
}
else if ($(this).hasClass('cp-app-drive-context-delete')) {
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
if (!APP.loggedIn) {
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
if (paths.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
filesOp.delete(pathsList, refresh);
}); });
return;
} }
moveElements(pathsList, [TRASH], false, refresh); else if ($(this).hasClass("cp-app-drive-context-properties")) {
} if (type === 'trash') {
else if ($(this).hasClass('cp-app-drive-context-deleteowned')) { var pPath = paths[0].path;
var msgD = Messages.fm_deleteOwnedPads; if (paths.length !== 1 || pPath.length !== 4) { return; }
UI.confirm(msgD, function(res) { var element = filesOp.find(pPath.slice(0,3)); // element containing the oldpath
$(window).focus(); var sPath = stringifyPath(element.path);
if (!res) { return; } UI.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true);
// Try to delete each selected pad from server, and delete from drive if no error
var n = nThen(function () {});
paths.forEach(function (p) {
var el = filesOp.find(p.path);
var data = filesOp.getFileData(el);
var parsed = Hash.parsePadUrl(data.href);
var channel = Util.base64ToHex(parsed.hashData.channel);
n = n.nThen(function (waitFor) {
sframeChan.query('Q_CONTACTS_CLEAR_OWNED_CHANNEL', channel,
waitFor(function (e) {
if (e) { return void console.error(e); }
filesOp.delete([p.path], refresh);
}));
});
});
});
return; return;
} }
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
el = filesOp.find(paths[0].path); el = filesOp.find(paths[0].path);
getProperties(el, function (e, $prop) { getProperties(el, function (e, $prop) {
@ -2757,98 +2798,45 @@ define([
var href = data.href; var href = data.href;
common.updateTags(href); common.updateTags(href);
} }
APP.hideMenu(); else if ($(this).hasClass("cp-app-drive-context-empty")) {
}); if (paths.length !== 1 || !paths[0].element
|| !filesOp.comparePath(paths[0].path, [TRASH])) {
$contentContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var path = $(this).data('path');
var onCreated = function (err, info) {
if (err === E_OVER_LIMIT) {
return void UI.alert(Messages.pinLimitDrive, null, true);
}
if (err) {
return void UI.alert(Messages.fm_error_cantPin);
}
APP.newFolder = info.newPath;
refresh();
};
if ($(this).hasClass("cp-app-drive-context-newfolder")) {
filesOp.addFolder(path, null, onCreated);
}
else if ($(this).hasClass("cp-app-drive-context-newdoc")) {
var type = $(this).data('type') || 'pad';
var path2 = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
common.sessionStorage.put(Constants.newPadPathKey, path2, function () {
common.openURL('/' + type + '/');
});
}
APP.hideMenu();
});
$trashTreeContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var paths = $(this).data('paths');
if (paths.length !== 1 || !paths[0].element || !filesOp.comparePath(paths[0].path, [TRASH])) {
log(Messages.fm_forbidden); log(Messages.fm_forbidden);
debug("Trash tree context menu on a forbidden or unexisting element. ", paths);
return; return;
} }
if ($(this).hasClass("cp-app-drive-context-empty")) {
UI.confirm(Messages.fm_emptyTrashDialog, function(res) { UI.confirm(Messages.fm_emptyTrashDialog, function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.emptyTrash(refresh); filesOp.emptyTrash(refresh);
}); });
} }
APP.hideMenu(); else if ($(this).hasClass("cp-app-drive-context-remove")) {
}); return void deletePaths(paths);
$trashContextMenu.on('click', 'a', function (e) {
e.stopPropagation();
var paths = $(this).data('paths');
if (paths.length === 0) {
log(Messages.fm_forbidden);
debug("Trash context menu on a forbidden or unexisting element. ", paths);
return;
}
var path = paths[0].path;
var name = paths[0].path[paths[0].path.length - 1];
if ($(this).hasClass("cp-app-drive-context-remove")) {
if (paths.length === 1) {
UI.confirm(Messages.fm_removePermanentlyDialog, function(res) {
if (!res) { return; }
filesOp.delete([path], refresh);
});
return;
}
var pathsList = [];
paths.forEach(function (p) { pathsList.push(p.path); });
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]);
UI.confirm(msg, function(res) {
if (!res) { return; }
filesOp.delete(pathsList, refresh);
});
} }
else if ($(this).hasClass("cp-app-drive-context-restore")) { else if ($(this).hasClass("cp-app-drive-context-restore")) {
if (paths.length !== 1) { return; } if (paths.length !== 1) { return; }
if (path.length === 4) { var restorePath = paths[0].path;
var el = filesOp.find(path); var restoreName = paths[0].path[paths[0].path.length - 1];
if (filesOp.isFile(el)) { if (restorePath.length === 4) {
name = filesOp.getTitle(el); var rEl = filesOp.find(restorePath);
if (filesOp.isFile(rEl)) {
restoreName = filesOp.getTitle(rEl);
} else { } else {
name = path[1]; restoreName = restorePath[1];
} }
} }
UI.confirm(Messages._getKey("fm_restoreDialog", [name]), function(res) { UI.confirm(Messages._getKey("fm_restoreDialog", [restoreName]), function(res) {
if (!res) { return; } if (!res) { return; }
filesOp.restore(path, refresh); filesOp.restore(restorePath, refresh);
}); });
} }
else if ($(this).hasClass("cp-app-drive-context-properties")) { else if ($(this).hasClass("cp-app-drive-context-openparent")) {
if (paths.length !== 1 || path.length !== 4) { return; } if (paths.length !== 1) { return; }
var element = filesOp.find(path.slice(0,3)); // element containing the oldpath var parentPath = paths[0].path.slice();
var sPath = stringifyPath(element.path); if (filesOp.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
UI.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true); else { parentPath.pop(); }
el = filesOp.find(paths[0].path);
APP.selectedFiles = [el];
APP.displayDirectory(parentPath);
} }
APP.hideMenu(); APP.hideMenu();
}); });

@ -9,6 +9,7 @@ define([
'/common/common-interface.js', '/common/common-interface.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/customize/messages.js', '/customize/messages.js',
'/customize/application_config.js',
'/bower_components/marked/marked.min.js', '/bower_components/marked/marked.min.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
@ -33,6 +34,7 @@ define([
UI, UI,
Realtime, Realtime,
Messages, Messages,
AppConfig,
Marked, Marked,
CodeMirror CodeMirror
) )
@ -478,6 +480,10 @@ define([
$(waitFor(UI.addLoadingScreen)); $(waitFor(UI.addLoadingScreen));
SFCommon.create(waitFor(function (c) { APP.common = common = c; })); SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
if (AppConfig.disableProfile) {
common.gotoURL('/drive/');
return;
}
APP.$container = $('#cp-sidebarlayout-container'); APP.$container = $('#cp-sidebarlayout-container');
APP.$toolbar = $('#cp-toolbar'); APP.$toolbar = $('#cp-toolbar');
APP.$leftside = $('<div>', {id: 'cp-sidebarlayout-leftside'}).appendTo(APP.$container); APP.$leftside = $('<div>', {id: 'cp-sidebarlayout-leftside'}).appendTo(APP.$container);

@ -70,6 +70,14 @@ define([
if (!AppConfig.dislayCreationScreen) { if (!AppConfig.dislayCreationScreen) {
delete categories.creation; delete categories.creation;
} }
if (AppConfig.disableFeedback) {
var feedbackIdx = categories.account.indexOf('cp-settings-userfeedback');
categories.account.splice(feedbackIdx, 1);
}
if (AppConfig.disableProfile) {
var displaynameIdx = categories.account.indexOf('cp-settings-displayname');
categories.account.splice(displaynameIdx, 1);
}
var create = {}; var create = {};
@ -155,8 +163,7 @@ define([
create['logout-everywhere'] = function () { create['logout-everywhere'] = function () {
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return; }
var $div = $('<div>', { 'class': 'cp-settings-logout-everywhere cp-sidebarlayout-element'}); var $div = $('<div>', { 'class': 'cp-settings-logout-everywhere cp-sidebarlayout-element'});
$('<label>', { 'for': 'cp-settings-logout-everywhere'}) $('<label>').text(Messages.settings_logoutEverywhereTitle).appendTo($div);
.text(Messages.settings_logoutEverywhereTitle).appendTo($div);
$('<span>', {'class': 'cp-sidebarlayout-description'}) $('<span>', {'class': 'cp-sidebarlayout-description'})
.text(Messages.settings_logoutEverywhere).appendTo($div); .text(Messages.settings_logoutEverywhere).appendTo($div);
var $button = $('<button>', { var $button = $('<button>', {

Loading…
Cancel
Save