Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
This commit is contained in:
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"
|
||||||
},
|
},
|
||||||
|
|||||||
177
storage/file.js
177
storage/file.js
@ -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);
|
||||||
};
|
};
|
||||||
@ -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
|
||||||
|
|||||||
115
www/common/application_config_internal.js
Normal file
115
www/common/application_config_internal.js
Normal 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;
|
||||||
|
});
|
||||||
@ -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,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) {
|
||||||
|
|||||||
@ -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'); }
|
||||||
|
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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…
x
Reference in New Issue
Block a user