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

This commit is contained in:
ansuz 2018-01-23 15:51:37 +01:00
commit 044384a576
16 changed files with 721 additions and 562 deletions

View File

@ -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;
}); });

View File

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

View File

@ -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"
}, },

View File

@ -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);
}, },
@ -331,4 +450,4 @@ module.exports.create = function (conf, cb) {
setInterval(function () { setInterval(function () {
flushUnusedChannels(env, function () { }); flushUnusedChannels(env, function () { });
}, 5000); }, 5000);
}; };

View File

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

View File

@ -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;
});

View File

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

View File

@ -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'},

View File

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

View File

@ -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'])
} }

View File

@ -238,55 +238,57 @@ 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);
var $button = $('<button>', { if (!Config.disableProfile) {
'class': 'fa fa-pencil cp-toolbar-userlist-name-edit', var $button = $('<button>', {
title: Messages.user_rename 'class': 'fa fa-pencil cp-toolbar-userlist-name-edit',
}).appendTo($nameSpan); title: Messages.user_rename
$button.hover(function (e) { e.preventDefault(); e.stopPropagation(); }); }).appendTo($nameSpan);
$button.mouseenter(function (e) { $button.hover(function (e) { e.preventDefault(); e.stopPropagation(); });
e.preventDefault(); $button.mouseenter(function (e) {
e.stopPropagation(); e.preventDefault();
window.setTimeout(function () { e.stopPropagation();
$button.parents().mouseleave(); window.setTimeout(function () {
$button.parents().mouseleave();
});
}); });
}); var $nameInput = $('<input>', {
var $nameInput = $('<input>', { 'class': 'cp-toolbar-userlist-name-input'
'class': 'cp-toolbar-userlist-name-input' }).val(name).appendTo($rightCol);
}).val(name).appendTo($rightCol); $button.click(function (e) {
$button.click(function (e) { e.stopPropagation();
e.stopPropagation(); $nameSpan.hide();
$nameSpan.hide(); $nameInput.show().focus().select();
$nameInput.show().focus().select(); editingUserName.state = true;
editingUserName.state = true; editingUserName.oldName = $nameInput.val();
editingUserName.oldName = $nameInput.val(); });
}); $nameInput.click(function (e) {
$nameInput.click(function (e) { e.stopPropagation();
e.stopPropagation(); });
}); $nameInput.on('keydown', function (e) {
$nameInput.on('keydown', function (e) { if (e.which === 13 || e.which === 27) {
if (e.which === 13 || e.which === 27) { $nameInput.hide();
$nameInput.hide(); $nameSpan.show();
$nameSpan.show(); $button.show();
$button.show(); editingUserName.state = false;
editingUserName.state = false; }
if (e.which === 13) {
var newName = $nameInput.val(); // TODO clean
$nameValue.text(newName);
setDisplayName(newName);
return;
}
if (e.which === 27) {
$nameValue.text(editingUserName.oldName);
return;
}
});
if (editingUserName.state) {
$button.click();
$nameInput.val(editingUserName.value);
$nameInput[0].setSelectionRange(editingUserName.select[0],
editingUserName.select[1]);
setTimeout(function () { $nameInput.focus(); });
} }
if (e.which === 13) {
var newName = $nameInput.val(); // TODO clean
$nameValue.text(newName);
setDisplayName(newName);
return;
}
if (e.which === 27) {
$nameValue.text(editingUserName.oldName);
return;
}
});
if (editingUserName.state) {
$button.click();
$nameInput.val(editingUserName.value);
$nameInput[0].setSelectionRange(editingUserName.select[0],
editingUserName.select[1]);
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) {

View File

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

View File

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

View File

@ -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'); }
var hide = []; $contextMenu.find('li').hide();
var hasFolder = false;
paths.forEach(function (p) { var show = [];
var path = p.path; var filter;
var $element = p.element;
if (path.length === 1) { if (type === "content") {
// Can't rename or delete root elements filter = function ($el, className) {
hide.push($menu.find('a.cp-app-drive-context-rename')); if (className === 'newfolder') { return; }
hide.push($menu.find('a.cp-app-drive-context-delete')); return AppConfig.availablePadTypes.indexOf($el.attr('data-type')) === -1;
} };
if (!APP.editable) { } else {
hide.push($menu.find('a.cp-app-drive-context-editable')); // In case of multiple selection, we must hide the option if at least one element
} // is not compatible
if (!isOwnDrive()) { var containsFolder = false;
hide.push($menu.find('a.cp-app-drive-context-own')); var hide = [];
} paths.forEach(function (p) {
if (!$element.is('.cp-app-drive-element-owned')) { var path = p.path;
hide.push($menu.find('a.cp-app-drive-context-deleteowned')); var $element = p.element;
} if (path.length === 1) {
if ($element.is('.cp-app-drive-element-notrash')) { // Can't rename or delete root elements
hide.push($menu.find('a.cp-app-drive-context-delete')); hide.push('delete');
} hide.push('rename');
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push($menu.find('a.cp-app-drive-context-newfolder'));
if ($element.is('.cp-app-drive-element-readonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-open'));
} else if ($element.is('.cp-app-drive-element-noreadonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-openro'));
} }
} else { if (!$element.is('.cp-app-drive-element-owned')) {
if (hasFolder) { hide.push('deleteowned');
// More than 1 folder selected: cannot create a new subfolder
hide.push($menu.find('a.cp-app-drive-context-newfolder'));
} }
hasFolder = true; if ($element.is('.cp-app-drive-element-notrash')) {
hide.push($menu.find('a.cp-app-drive-context-openro')); // We can't delete elements in virtual categories
hide.push($menu.find('a.cp-app-drive-context-properties')); hide.push('delete');
hide.push($menu.find('a.cp-app-drive-context-hashtag')); } else {
// We can only open parent in virtual categories
hide.push('openparent');
}
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push('newfolder');
if ($element.is('.cp-app-drive-element-readonly')) {
hide.push('open'); // Remove open 'edit' mode
} else if ($element.is('.cp-app-drive-element-noreadonly')) {
hide.push('openro'); // Remove open 'view' mode
}
} else { // it's a folder
if (containsFolder) {
// More than 1 folder selected: cannot create a new subfolder
hide.push('newfolder');
}
containsFolder = true;
hide.push('openro');
hide.push('properties');
hide.push('hashtag');
}
// If we're in the trash, hide restore and properties for non-root elements
if (type === "trash" && path && path.length > 4) {
hide.push('restore');
hide.push('properties');
}
});
if (paths.length > 1) {
hide.push('restore');
hide.push('properties');
hide.push('rename');
hide.push('openparent');
} }
// If we're in the trash, hide restore and properties for non-root elements if (containsFolder && paths.length > 1) {
if ($menu.find('a.cp-app-drive-context-restore').length && path && path.length > 4) { // Cannot open multiple folders
hide.push($menu.find('a.cp-app-drive-context-restore')); hide.push('open');
hide.push($menu.find('a.cp-app-drive-context-properties'));
} }
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);
}); });
if (paths.length > 1) { return filtered;
hide.push($menu.find('a.cp-app-drive-context-restore'));
hide.push($menu.find('a.cp-app-drive-context-properties'));
hide.push($menu.find('a.cp-app-drive-context-rename'));
}
if (hasFolder && paths.length > 1) {
// Cannot open multiple folders
hide.push($menu.find('a.cp-app-drive-context-open'));
}
return hide;
}; };
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,101 +1046,49 @@ 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) {
APP.hideMenu(); return function (e) {
e.stopPropagation(); APP.hideMenu();
e.stopPropagation();
var $element = findDataHolder($(e.target)); var paths;
if (!$element.length) { if (type === 'content') {
logError("Unable to locate the .element tag", e.target); paths = [{path: $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path')}];
$menu.hide(); if (!paths) { return; }
log(Messages.fm_contextMenuError); removeSelected();
return false; } else {
} var $element = findDataHolder($(e.target));
if (!$element.hasClass('cp-app-drive-element-selected')) { //paths.length === 1) { if (type === 'trash' && !$element.data('path')) { return; }
onElementClick(undefined, $element);
}
var paths = getSelectedPaths($element); if (!$element.length) {
logError("Unable to locate the .element tag", e.target);
log(Messages.fm_contextMenuError);
return false;
}
var toHide = filterContextMenu($menu, paths); if (!$element.hasClass('cp-app-drive-element-selected')) {
toHide.forEach(function ($a) { onElementClick(undefined, $element);
$a.parent('li').hide(); }
});
displayMenu(e, $menu); paths = getSelectedPaths($element);
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);
//$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) {
APP.hideMenu();
e.stopPropagation();
var path = $(e.target).closest('#' + FOLDER_CONTENT_ID).data('path');
if (!path) { return; }
var $menu = $contentContextMenu;
removeSelected();
if (!APP.editable) {
$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 (AppConfig.availablePadTypes.indexOf($(el).attr('data-type')) === -1) {
$(el).hide();
} }
});
displayMenu(e, $menu); $contextMenu.attr('data-menu-type', type);
if ($menu.find('li:visible').length === 0) { filterContextMenu(type, paths);
debug("No visible element in the context menu. Abort.");
$menu.hide();
return true;
}
$menu.find('a').data('path', path); displayMenu(e);
return false;
if ($contextMenu.find('li:visible').length === 0) {
debug("No visible element in the context menu. Abort.");
$contextMenu.hide();
return true;
}
$contextMenu.data('paths', paths);
return false;
};
}; };
var getElementName = function (path) { var getElementName = function (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);
} }
@ -2637,61 +2749,6 @@ define([
$element.dblclick(); $element.dblclick();
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-openro')) {
paths.forEach(function (p) {
var el = filesOp.find(p.path);
if (filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el);
openFile(null, roUrl);
});
}
else if ($(this).hasClass('cp-app-drive-context-newfolder')) {
if (paths.length !== 1) { return; }
var onCreated = function (err, info) {
if (err) { return void logError(err); }
APP.newFolder = info.newPath;
APP.displayDirectory(paths[0].path);
};
filesOp.addFolder(paths[0].path, null, onCreated);
}
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-openro')) {
paths.forEach(function (p) { paths.forEach(function (p) {
var el = filesOp.find(p.path); var el = filesOp.find(p.path);
@ -2701,47 +2758,31 @@ define([
openFile(null, roUrl); openFile(null, roUrl);
}); });
} }
else if ($(this).hasClass('cp-app-drive-context-delete')) { else if ($(this).hasClass('cp-app-drive-context-newfolder')) {
var pathsList = []; if (paths.length !== 1) { return; }
paths.forEach(function (p) { pathsList.push(p.path); }); var onFolderCreated = function (err, info) {
if (!APP.loggedIn) { if (err) { return void logError(err); }
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [paths.length]); APP.newFolder = info.newPath;
if (paths.length === 1) { APP.displayDirectory(paths[0].path);
msg = Messages.fm_removePermanentlyDialog; };
} filesOp.addFolder(paths[0].path, null, onFolderCreated);
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-deleteowned')) { else if ($(this).hasClass("cp-app-drive-context-newdoc")) {
var msgD = Messages.fm_deleteOwnedPads; var ntype = $(this).data('type') || 'pad';
UI.confirm(msgD, function(res) { var path2 = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
$(window).focus(); common.sessionStorage.put(Constants.newPadPathKey, path2, function () {
if (!res) { return; } common.openURL('/' + ntype + '/');
// 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;
} }
else if ($(this).hasClass("cp-app-drive-context-properties")) { else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (type === 'trash') {
var pPath = paths[0].path;
if (paths.length !== 1 || pPath.length !== 4) { return; }
var element = filesOp.find(pPath.slice(0,3)); // element containing the oldpath
var sPath = stringifyPath(element.path);
UI.alert('<strong>' + Messages.fm_originalPath + "</strong>:<br>" + sPath, undefined, true);
return;
}
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) { log(Messages.fm_forbidden);
e.stopPropagation(); return;
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);
debug("Trash tree context menu on a forbidden or unexisting element. ", paths);
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();
}); });

View File

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

View File

@ -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>', {