mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-20 06:29:53 +01:00
Merge pull request #1 from ether/develop
Update to most recent etherpad-lite version
This commit is contained in:
commit
f2836125dc
32 changed files with 574 additions and 171 deletions
|
@ -4,7 +4,7 @@
|
|||
# About
|
||||
Etherpad is a really-real time collaborative editor maintained by the Etherpad Community.
|
||||
|
||||
Etherpad is written in JavaScript(99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage.
|
||||
Etherpad is written in JavaScript (99.9%) on both the server and client so it's easy for developers to maintain and add new features. Because of this Etherpad has tons of customizations that you can leverage.
|
||||
|
||||
Etherpad is designed to be easily embeddable and provides a [HTTP API](https://github.com/ether/etherpad-lite/wiki/HTTP-API)
|
||||
that allows your web application to manage pads, users and groups. It is recommended to use the [available client implementations](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) in order to interact with this API.
|
||||
|
|
|
@ -46,13 +46,14 @@ fi
|
|||
#check node version
|
||||
NODE_VERSION=$(node --version)
|
||||
NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2)
|
||||
NODE_V_MAIN=$(echo $NODE_VERSION | cut -d "." -f 1)
|
||||
#iojs version checking added
|
||||
if hash iojs 2>/dev/null; then
|
||||
IOJS_VERSION=$(iojs --version)
|
||||
fi
|
||||
if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ]; then
|
||||
if [ ! $NODE_V_MINOR = "v0.10" ] && [ ! $NODE_V_MINOR = "v0.11" ] && [ ! $NODE_V_MINOR = "v0.12" ] && [ ! $NODE_V_MAIN = "v4" ] && [ ! $NODE_V_MAIN = "v5" ]; then
|
||||
if [ ! $IOJS_VERSION ]; then
|
||||
echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need v0.10.x, v0.11.x or v0.12.x" >&2
|
||||
echo "You're running a wrong version of node, or io.js is not installed. You're using $NODE_VERSION, we need node v0.10.x or higher" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -6,11 +6,6 @@ cd /D "%~dp0\.."
|
|||
:: Is node installed?
|
||||
cmd /C node -e "" || ( echo "Please install node.js ( http://nodejs.org )" && exit /B 1 )
|
||||
|
||||
echo _
|
||||
echo Checking node version...
|
||||
set check_version="if(['10','11','12'].indexOf(process.version.split('.')[1]) === -1 && process.version.split('.')[0] !== '1') { console.log('You are running a wrong version of Node. Etherpad requires v0.10+'); process.exit(1) }"
|
||||
cmd /C node -e %check_version% || exit /B 1
|
||||
|
||||
echo _
|
||||
echo Ensure that all dependencies are up to date... If this is the first time you have run Etherpad please be patient.
|
||||
cmd /C npm install src/ --loglevel warn || exit /B 1
|
||||
|
|
|
@ -79,6 +79,9 @@ async.series([
|
|||
newPad.pool.numToAttrib = oldPad.pool.numToAttrib;
|
||||
for(var curRevNum = 0; curRevNum <= newRevHead; curRevNum++) {
|
||||
db.db.get("pad:" + padId + ":revs:" + curRevNum, function(err, rev) {
|
||||
if (rev.meta) {
|
||||
throw "The specified revision number could not be found.";
|
||||
}
|
||||
var newRevNum = ++newPad.head;
|
||||
var newRevId = "pad:" + newPad.id + ":revs:" + newRevNum;
|
||||
db.db.set(newRevId, rev);
|
||||
|
|
|
@ -339,3 +339,14 @@ Things in context:
|
|||
|
||||
This hook is provided to allow author highlight style to be modified.
|
||||
Registered hooks should return 1 if the plugin handles highlighting. If no plugin returns 1, the core will use the default background-based highlighting.
|
||||
|
||||
## aceSelectionChanged
|
||||
Called from: src/static/js/ace2_inner.js
|
||||
|
||||
Things in context:
|
||||
|
||||
1. rep - information about where the user's cursor is
|
||||
2. documentAttributeManager - information about attributes in the document
|
||||
|
||||
This hook allows a plugin to react to a cursor or selection change,
|
||||
perhaps to update a UI element based on the style at the cursor location.
|
||||
|
|
|
@ -357,7 +357,7 @@ Things in context:
|
|||
|
||||
1. Pad object
|
||||
|
||||
This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array should be returned.
|
||||
This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. If tags are stored as `['color', 'red']` on the attribute pool, use `exportHtmlAdditionalTagsWithData` instead. An Array should be returned.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
@ -368,6 +368,24 @@ exports.exportHtmlAdditionalTags = function(hook, pad, cb){
|
|||
};
|
||||
```
|
||||
|
||||
## exportHtmlAdditionalTagsWithData
|
||||
Called from src/node/utils/ExportHtml.js
|
||||
|
||||
Things in context:
|
||||
|
||||
1. Pad object
|
||||
|
||||
Identical to `exportHtmlAdditionalTags`, but for tags that are stored with an specific value (not simply `true`) on the attribute pool. For example `['color', 'red']`, instead of `['bold', true]`. This hook will allow a plug-in developer to include more properties and attributes to support during HTML Export. An Array of arrays should be returned. The exported HTML will contain tags like `<span data-color="red">` for the content where attributes are `['color', 'red']`.
|
||||
|
||||
Example:
|
||||
```
|
||||
// Add the props to be supported in export
|
||||
exports.exportHtmlAdditionalTagsWithData = function(hook, pad, cb){
|
||||
var padId = pad.id;
|
||||
cb([["color", "red"], ["color", "blue"]]);
|
||||
};
|
||||
```
|
||||
|
||||
## userLeave
|
||||
Called from src/node/handler/PadMessageHandler.js
|
||||
|
||||
|
@ -384,3 +402,20 @@ exports.userLeave = function(hook, session, callback) {
|
|||
console.log('%s left pad %s', session.author, session.padId);
|
||||
};
|
||||
```
|
||||
|
||||
### clientReady
|
||||
Called from src/node/handler/PadMessageHandler.js
|
||||
|
||||
This in context:
|
||||
|
||||
1. message
|
||||
|
||||
This hook gets called when handling a CLIENT_READY which is the first message from the client to the server.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
exports.clientReady = function(hook, message) {
|
||||
console.log('Client has entered the pad' + message.padId);
|
||||
};
|
||||
```
|
||||
|
|
|
@ -86,10 +86,14 @@
|
|||
may cause problems during deployment. Set to 0 to disable caching */
|
||||
"maxAge" : 21600, // 60 * 60 * 6 = 6 hours
|
||||
|
||||
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
|
||||
/* This is the absolute path to the Abiword executable. Setting it to null, disables abiword.
|
||||
Abiword is needed to advanced import/export features of pads*/
|
||||
"abiword" : null,
|
||||
|
||||
/* This is the absolute path to the soffice executable. Setting it to null, disables LibreOffice exporting.
|
||||
LibreOffice can be used in lieu of Abiword to export pads */
|
||||
"soffice" : null,
|
||||
|
||||
/* This is the path to the Tidy executable. Setting it to null, disables Tidy.
|
||||
Tidy is used to improve the quality of exported pads*/
|
||||
"tidyHtml" : null,
|
||||
|
@ -131,6 +135,11 @@
|
|||
// Allow Load Testing tools to hit the Etherpad Instance. Warning this will disable security on the instance.
|
||||
"loadTest": false,
|
||||
|
||||
// Disable indentation on new line when previous line ends with some special chars (':', '[', '(', '{')
|
||||
/*
|
||||
"indentationOnNewLine": false,
|
||||
*/
|
||||
|
||||
/* The toolbar buttons configuration.
|
||||
"toolbar": {
|
||||
"left": [
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"Alami",
|
||||
"Meno25",
|
||||
"Test Create account",
|
||||
"محمد أحمد عبد الفتاح"
|
||||
"محمد أحمد عبد الفتاح",
|
||||
"Haytham morsy"
|
||||
]
|
||||
},
|
||||
"index.newPad": "باد جديد",
|
||||
|
@ -97,6 +98,9 @@
|
|||
"timeslider.exportCurrent": "تصدير النسخة الحالية ك:",
|
||||
"timeslider.version": "إصدار {{version}}",
|
||||
"timeslider.saved": "محفوظ {{month}} {{day}}, {{year}}",
|
||||
"timeslider.playPause": "تشغيل / إيقاف مؤقت محتويات الباد",
|
||||
"timeslider.backRevision": "عد إلى مراجعة في هذه الباد",
|
||||
"timeslider.forwardRevision": "انطلق إلى مراجعة في هذه الباد",
|
||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "يناير",
|
||||
"timeslider.month.february": "فبراير",
|
||||
|
|
|
@ -132,7 +132,7 @@
|
|||
"pad.impexp.confirmimport": "Al importar un archivo se borrará el contenido actual del pad. ¿Estás seguro de que quieres continuar?",
|
||||
"pad.impexp.convertFailed": "No pudimos importar este archivo. Inténtalo con un formato diferente o copia y pega manualmente.",
|
||||
"pad.impexp.padHasData": "No hemos podido importar este archivo porque este pad ya ha tenido cambios. Importa a un nuevo pad.",
|
||||
"pad.impexp.uploadFailed": "El envío falló. Intentalo de nuevo.",
|
||||
"pad.impexp.uploadFailed": "El envío falló. Inténtalo de nuevo.",
|
||||
"pad.impexp.importfailed": "Fallo al importar",
|
||||
"pad.impexp.copypaste": "Intenta copiar y pegar",
|
||||
"pad.impexp.exportdisabled": "La exportación al formato {{type}} está desactivada. Contacta a tu administrador de sistemas."
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Denö",
|
||||
"Mashoi7"
|
||||
"Mashoi7",
|
||||
"Ilja.mos"
|
||||
]
|
||||
},
|
||||
"pad.toolbar.underline.title": "Alleviivua (Ctrl+U)",
|
||||
|
@ -36,7 +37,7 @@
|
|||
"timeslider.month.january": "pakkaskuudu",
|
||||
"timeslider.month.february": "tuhukuudu",
|
||||
"timeslider.month.march": "kevätkuudu",
|
||||
"timeslider.month.april": "kevätkuudu",
|
||||
"timeslider.month.april": "sulakuudu",
|
||||
"timeslider.month.may": "oraskuudu",
|
||||
"timeslider.month.june": "kezäkuudu",
|
||||
"timeslider.month.july": "heinykuudu",
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Aalam",
|
||||
"Babanwalia"
|
||||
"Babanwalia",
|
||||
"ਪ੍ਰਚਾਰਕ"
|
||||
]
|
||||
},
|
||||
"index.newPad": "ਨਵਾਂ ਪੈਡ",
|
||||
|
@ -10,29 +11,31 @@
|
|||
"pad.toolbar.bold.title": "ਗੂੜ੍ਹਾ (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "ਤਿਰਛਾ (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "ਹੇਠਾਂ-ਰੇਖਾ (Ctrl-U)",
|
||||
"pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ ਵਿਨੋ",
|
||||
"pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਲਿਸਟ",
|
||||
"pad.toolbar.strikethrough.title": "ਵਿੰਨ੍ਹੋ (Ctrl+5)",
|
||||
"pad.toolbar.ol.title": "ਲੜੀਵਾਰ ਸੂਚੀ",
|
||||
"pad.toolbar.ul.title": "ਬਿਨ-ਲੜੀਬੱਧ ਸੂਚੀ",
|
||||
"pad.toolbar.indent.title": "ਹਾਸ਼ੀਏ ਤੋਂ ਪਰ੍ਹੇ (ਟੈਬ)",
|
||||
"pad.toolbar.unindent.title": "ਹਾਸ਼ੀਏ ਵੱਲ (ਸ਼ਿਫ਼ਟ+ਟੈਬ)",
|
||||
"pad.toolbar.undo.title": "ਵਾਪਸ (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "ਪਰਤਾਓ (Ctrl-Y)",
|
||||
"pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ",
|
||||
"pad.toolbar.clearAuthorship.title": "ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰੋ (Ctrl+Shift+C)",
|
||||
"pad.toolbar.import_export.title": "ਵੱਖ-ਵੱਖ ਫਾਇਲ ਫਾਰਮੈਟ ਤੋਂ/ਵਿੱਚ ਇੰਪੋਰਟ/ਐਕਸਪੋਰਟ ਕਰੋ",
|
||||
"pad.toolbar.timeslider.title": "ਸਮਾਂ-ਲਕੀਰ",
|
||||
"pad.toolbar.savedRevision.title": "ਰੀਵਿਜ਼ਨ ਸੰਭਾਲੋ",
|
||||
"pad.toolbar.settings.title": "ਸੈਟਿੰਗ",
|
||||
"pad.toolbar.embed.title": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਤੇ ਇੰਬੈੱਡ ਕਰੋ",
|
||||
"pad.toolbar.showusers.title": "ਇਹ ਪੈਡ ਉੱਤੇ ਯੂਜ਼ਰ ਵੇਖਾਓ",
|
||||
"pad.colorpicker.save": "ਸਾਂਭੋ",
|
||||
"pad.colorpicker.save": "ਸੰਭਾਲੋ",
|
||||
"pad.colorpicker.cancel": "ਰੱਦ ਕਰੋ",
|
||||
"pad.loading": "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
||||
"pad.noCookie": "ਕੂਕੀਜ਼ ਨਹੀਂ ਲੱਭੀਅਾਂ। ਕਿਰਪਾ ਕਰਕੇ ਬ੍ਰਾੳੂਜ਼ਰ ਵਿੱਚ ਕੂਕੀਜ਼ ਲਾਗੂ ਕਰੋ।",
|
||||
"pad.passwordRequired": "ਇਹ ਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ",
|
||||
"pad.permissionDenied": "ਇਹ ਪੈਡ ਵਰਤਨ ਲਈ ਤੁਹਾਨੂੰ ਅਧਿਕਾਰ ਨਹੀਂ ਹਨ",
|
||||
"pad.wrongPassword": "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਗਲਤੀ ਸੀ",
|
||||
"pad.settings.padSettings": "ਪੈਡ ਸੈਟਿੰਗ",
|
||||
"pad.settings.myView": "ਮੇਰੀ ਝਲਕ",
|
||||
"pad.settings.stickychat": "ਹਮੇਸ਼ਾ ਸਕਰੀਨ ਉੱਤੇ ਗੱਲ ਕਰੋ",
|
||||
"pad.settings.chatandusers": "ਗੱਲ-ਬਾਤ ਅਤੇ ਵਰਤੋਂਕਾਰ ਦਿਖਾਵੋ",
|
||||
"pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ",
|
||||
"pad.settings.linenocheck": "ਲਾਈਨ ਨੰਬਰ",
|
||||
"pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?",
|
||||
|
@ -45,10 +48,11 @@
|
|||
"pad.importExport.import": "ਕੋਈ ਵੀ ਟੈਕਸਟ ਫਾਇਲ ਜਾਂ ਦਸਤਾਵੇਜ਼ ਅੱਪਲੋਡ ਕਰੋ",
|
||||
"pad.importExport.importSuccessful": "ਸਫ਼ਲ!",
|
||||
"pad.importExport.export": "ਮੌਜੂਦਾ ਪੈਡ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
||||
"pad.importExport.exportetherpad": "ੲੈਥਰਪੈਡ",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "ਸਧਾਰਨ ਟੈਕਸਟ",
|
||||
"pad.importExport.exportword": "ਮਾਈਕਰੋਸਾਫਟ ਵਰਡ",
|
||||
"pad.importExport.exportpdf": "ਪੀਡੀਐਫ",
|
||||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (ਓਪਨ ਡੌਕੂਮੈਂਟ ਫਾਰਮੈਟ)",
|
||||
"pad.importExport.abiword.innerHTML": "ਤੁਸੀਂ ਸਿਰਫ਼ ਸਾਦੀਆਂ ਲਿਖਤੀ ਜਾਂ ਐੱਚ.ਟੀ.ਐੱਮ.ਐੱਲ. ਰੂਪ-ਰੇਖਾਵਾਂ ਤੋਂ ਦਰਾਮਦ ਕਰ ਸਕਦੇ ਹੋ। ਹੋਰ ਉੱਨਤ ਦਰਾਮਦੀ ਗੁਣਾਂ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">ਐਬੀਵਰਡ ਥਾਪੋ</a>।",
|
||||
"pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।",
|
||||
|
@ -88,8 +92,11 @@
|
|||
"timeslider.toolbar.authorsList": "ਕੋਈ ਲੇਖਕ ਨਹੀਂ",
|
||||
"timeslider.toolbar.exportlink.title": "ਐਕਸਪੋਰਟ",
|
||||
"timeslider.exportCurrent": "ਮੌਜੂਦਾ ਵਰਜਨ ਇੰਝ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
||||
"timeslider.version": "ਵਰਜਨ {{version}}",
|
||||
"timeslider.version": "ਵਰਜ਼ਨ {{version}}",
|
||||
"timeslider.saved": "{{day}} {{month}} {{year}} ਨੂੰ ਸੰਭਾਲਿਆ",
|
||||
"timeslider.playPause": "ਪੈਡ ਸਮੱਗਰੀ ਚਲਾਓ / ਵਿਰਾਮ ਕਰੋ",
|
||||
"timeslider.backRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਪਿਛਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ",
|
||||
"timeslider.forwardRevision": "ਇਸ ਪੈਡ ਵਿੱਚ ਅਗਲੇ ਰੀਵਿਜ਼ਨ ਤੇ ਜਾਓ",
|
||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "ਜਨਵਰੀ",
|
||||
"timeslider.month.february": "ਫ਼ਰਵਰੀ",
|
||||
|
@ -118,5 +125,5 @@
|
|||
"pad.impexp.uploadFailed": "ਅੱਪਲੋਡ ਲਈ ਫੇਲ੍ਹ ਹੈ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।",
|
||||
"pad.impexp.importfailed": "ਇੰਪੋਰਟ ਫੇਲ੍ਹ ਹੈ",
|
||||
"pad.impexp.copypaste": "ਕਾਪੀ ਕਰੋ ਚੇਪੋ ਜੀ",
|
||||
"pad.impexp.exportdisabled": "{{type}} ਰੂਪ-ਰੇਖਾ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪ੍ਰਬੰਧਕ ਨਾਲ਼ ਰਾਬਤਾ ਬਣਾਉ।"
|
||||
"pad.impexp.exportdisabled": "{{type}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"Hedwig",
|
||||
"ImGelu",
|
||||
"Minisarm",
|
||||
"Strainu"
|
||||
"Strainu",
|
||||
"Wintereu"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Pad nou",
|
||||
|
@ -21,6 +22,7 @@
|
|||
"pad.toolbar.import_export.title": "Importă/Exportă din/în diferite formate",
|
||||
"pad.toolbar.savedRevision.title": "Salvează revizia",
|
||||
"pad.toolbar.settings.title": "Setări",
|
||||
"pad.toolbar.showusers.title": "Arată utilizatorii de pe acest pad",
|
||||
"pad.colorpicker.save": "Salvează",
|
||||
"pad.colorpicker.cancel": "Anulează",
|
||||
"pad.loading": "Se încarcă...",
|
||||
|
@ -46,10 +48,10 @@
|
|||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.modals.connected": "Conectat.",
|
||||
"pad.modals.reconnecting": "Se reconectează la pad-ul tău..",
|
||||
"pad.modals.reconnecting": "Se reconectează la pad-ul dumneavoastră..",
|
||||
"pad.modals.forcereconnect": "Forțează reconectarea",
|
||||
"pad.modals.userdup": "Deschis în altă fereastră",
|
||||
"pad.modals.userdup.advice": "Reconectează pentru a folosi această fereastră în schimb",
|
||||
"pad.modals.userdup.advice": "Reconectați-vă dacă doriți să utilizați această fereastră.",
|
||||
"pad.modals.unauth": "Nu ești autorizat",
|
||||
"pad.modals.initsocketfail": "Serverul nu este disponibil.",
|
||||
"pad.modals.initsocketfail.explanation": "Nu s-a putut conecta la serverul de sincronizare.",
|
||||
|
@ -61,12 +63,12 @@
|
|||
"pad.share": "Distribuie acest pad",
|
||||
"pad.share.readonly": "Doar în citire",
|
||||
"pad.share.link": "Legătură",
|
||||
"pad.share.emebdcode": "Încorporează URL-ul",
|
||||
"pad.share.emebdcode": "Adresa URL încorporată",
|
||||
"pad.chat": "Chat",
|
||||
"pad.chat.title": "Deschide chat-ul pentru acest pad.",
|
||||
"pad.chat.loadmessages": "Încarcă mai multe mesaje",
|
||||
"timeslider.toolbar.returnbutton": "Înapoi la pad",
|
||||
"timeslider.toolbar.authors": "Aurori:",
|
||||
"timeslider.toolbar.authors": "Autori:",
|
||||
"timeslider.toolbar.authorsList": "Niciun autor",
|
||||
"timeslider.toolbar.exportlink.title": "Exportă",
|
||||
"timeslider.exportCurrent": "Exportă versiunea curentă ca:",
|
||||
|
@ -85,7 +87,7 @@
|
|||
"timeslider.month.october": "octombrie",
|
||||
"timeslider.month.november": "noiembrie",
|
||||
"timeslider.month.december": "decembrie",
|
||||
"pad.userlist.entername": "Introdu numele tău",
|
||||
"pad.userlist.entername": "Introduceți numele dumneavoastră",
|
||||
"pad.userlist.unnamed": "fără nume",
|
||||
"pad.userlist.guest": "Oaspete",
|
||||
"pad.userlist.deny": "Respinge",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"Kosovastar"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Bllok i Ri",
|
||||
"index.newPad": "Bllok i ri",
|
||||
"index.createOpenPad": "ose krijoni/hapni një Bllok me emrin:",
|
||||
"pad.toolbar.bold.title": "Të trasha (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "Të pjerrëta (Ctrl-I)",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"pad.toolbar.strikethrough.title": "Hequrvije (Ctrl+5)",
|
||||
"pad.toolbar.ol.title": "Listë e renditur (Ctrl+Shift+N)",
|
||||
"pad.toolbar.ul.title": "Listë e parenditur (Ctrl+Shift+L)",
|
||||
"pad.toolbar.indent.title": "Brendazi (TAB)",
|
||||
"pad.toolbar.indent.title": "E dhëmbëzuar (TAB)",
|
||||
"pad.toolbar.unindent.title": "Jashtazi (Shift+TAB)",
|
||||
"pad.toolbar.undo.title": "Zhbëje (Ctrl-Z)",
|
||||
"pad.toolbar.redo.title": "Ribëje (Ctrl-Y)",
|
||||
|
@ -21,7 +21,7 @@
|
|||
"pad.toolbar.import_export.title": "Importoni/Eksportoni nga/në formate të tjera kartelash",
|
||||
"pad.toolbar.timeslider.title": "Rrjedha kohore",
|
||||
"pad.toolbar.savedRevision.title": "Ruaje Rishikimin",
|
||||
"pad.toolbar.settings.title": "Rregullime",
|
||||
"pad.toolbar.settings.title": "Parametrat",
|
||||
"pad.toolbar.embed.title": "Ndajeni me të tjerët dhe Trupëzojeni këtë bllok",
|
||||
"pad.toolbar.showusers.title": "Shfaq përdoruesit në këtë bllok",
|
||||
"pad.colorpicker.save": "Ruaje",
|
||||
|
@ -45,6 +45,7 @@
|
|||
"pad.importExport.import": "Ngarkoni cilëndo kartelë teksti ose dokument",
|
||||
"pad.importExport.importSuccessful": "Me sukses!",
|
||||
"pad.importExport.export": "Eksportojeni bllokun e tanishëm si:",
|
||||
"pad.importExport.exportetherpad": "Etherpad",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "Tekst të thjeshtë",
|
||||
"pad.importExport.exportword": "Microsoft Word",
|
||||
|
@ -58,7 +59,7 @@
|
|||
"pad.modals.userdup.explanation": "Ky bllok duket se gjendet i hapur në më shumë se një dritare shfletuesi në këtë kompjuter.",
|
||||
"pad.modals.userdup.advice": "Rilidhuni që të përdoret kjo dritare.",
|
||||
"pad.modals.unauth": "I paautorizuar",
|
||||
"pad.modals.unauth.explanation": "Ndërkohë që shihnit këtë dritare, lejet tuaja kanë ndryshuar. Provoni të rilidheni.",
|
||||
"pad.modals.unauth.explanation": "Ndërkohë që sheh këtë dritare, lejet e tua kanë ndryshuar. Provo të rilidhesh.",
|
||||
"pad.modals.looping.explanation": "Ka probleme komunikimi me shërbyesin e njëkohësimit.",
|
||||
"pad.modals.looping.cause": "Ndoshta jeni lidhur përmes një firewall-i ose ndërmjetësi të papërputhshëm.",
|
||||
"pad.modals.initsocketfail": "Nuk kapet dot shërbyesi.",
|
||||
|
@ -90,6 +91,7 @@
|
|||
"timeslider.exportCurrent": "Eksportojeni versionin e tanishëm si:",
|
||||
"timeslider.version": "Versioni {{version}}",
|
||||
"timeslider.saved": "Ruajtur më {{month}} {{day}}, {{year}}",
|
||||
"timeslider.playPause": "Luaj përmbajtjet e Pad / Pauzo",
|
||||
"timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||
"timeslider.month.january": "Janar",
|
||||
"timeslider.month.february": "Shkurt",
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
"pad.modals.badChangeset.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。如果您认为这是错误,请联系服务管理员。要继续编辑,请尝试重新连接。",
|
||||
"pad.modals.corruptPad.explanation": "您试图连接的记事本已损坏。",
|
||||
"pad.modals.corruptPad.cause": "这可能是因为服务器配置的错误或者其他未预料到的行为。请联系服务管理员。",
|
||||
"pad.modals.deleted": "已刪除。",
|
||||
"pad.modals.deleted": "已删除。",
|
||||
"pad.modals.deleted.explanation": "此记事本已被移除。",
|
||||
"pad.modals.disconnected": "您已断开连接。",
|
||||
"pad.modals.disconnected.explanation": "到服务器的连接已丢失",
|
||||
|
|
|
@ -307,6 +307,38 @@ exports.setText = function(padID, text, callback)
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
appendText(padID, text) appends text to a pad
|
||||
|
||||
Example returns:
|
||||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
*/
|
||||
exports.appendText = function(padID, text, callback)
|
||||
{
|
||||
//text is required
|
||||
if(typeof text != "string")
|
||||
{
|
||||
callback(new customError("text is no string","apierror"));
|
||||
return;
|
||||
}
|
||||
|
||||
//get the pad
|
||||
getPadSafe(padID, true, function(err, pad)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
pad.appendText(text);
|
||||
|
||||
//update the clients on the pad
|
||||
padMessageHandler.updatePadClients(pad, callback);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
getHTML(padID, [rev]) returns the html of a pad
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ exports.createAuthor = function(name, callback)
|
|||
var author = "a." + randomString(16);
|
||||
|
||||
//create the globalAuthors db entry
|
||||
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
||||
var authorObj = {"colorId" : Math.floor(Math.random()*(exports.getColorPalette().length)), "name": name, "timestamp": new Date().getTime()};
|
||||
|
||||
//set the global author db entry
|
||||
db.set("globalAuthor:" + author, authorObj);
|
||||
|
|
|
@ -303,6 +303,19 @@ Pad.prototype.setText = function setText(newText) {
|
|||
this.appendRevision(changeset);
|
||||
};
|
||||
|
||||
Pad.prototype.appendText = function appendText(newText) {
|
||||
//clean the new text
|
||||
newText = exports.cleanText(newText);
|
||||
|
||||
var oldText = this.text();
|
||||
|
||||
//create the changeset
|
||||
var changeset = Changeset.makeSplice(oldText, oldText.length, 0, newText);
|
||||
|
||||
//append the changeset
|
||||
this.appendRevision(changeset);
|
||||
};
|
||||
|
||||
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||
this.chatHead++;
|
||||
//save the chat entry in the database
|
||||
|
|
|
@ -444,10 +444,61 @@ var version =
|
|||
, "getChatHead" : ["padID"]
|
||||
, "restoreRevision" : ["padID", "rev"]
|
||||
}
|
||||
, "1.2.13":
|
||||
{ "createGroup" : []
|
||||
, "createGroupIfNotExistsFor" : ["groupMapper"]
|
||||
, "deleteGroup" : ["groupID"]
|
||||
, "listPads" : ["groupID"]
|
||||
, "listAllPads" : []
|
||||
, "createDiffHTML" : ["padID", "startRev", "endRev"]
|
||||
, "createPad" : ["padID", "text"]
|
||||
, "createGroupPad" : ["groupID", "padName", "text"]
|
||||
, "createAuthor" : ["name"]
|
||||
, "createAuthorIfNotExistsFor": ["authorMapper" , "name"]
|
||||
, "listPadsOfAuthor" : ["authorID"]
|
||||
, "createSession" : ["groupID", "authorID", "validUntil"]
|
||||
, "deleteSession" : ["sessionID"]
|
||||
, "getSessionInfo" : ["sessionID"]
|
||||
, "listSessionsOfGroup" : ["groupID"]
|
||||
, "listSessionsOfAuthor" : ["authorID"]
|
||||
, "getText" : ["padID", "rev"]
|
||||
, "setText" : ["padID", "text"]
|
||||
, "getHTML" : ["padID", "rev"]
|
||||
, "setHTML" : ["padID", "html"]
|
||||
, "getAttributePool" : ["padID"]
|
||||
, "getRevisionsCount" : ["padID"]
|
||||
, "getSavedRevisionsCount" : ["padID"]
|
||||
, "listSavedRevisions" : ["padID"]
|
||||
, "saveRevision" : ["padID", "rev"]
|
||||
, "getRevisionChangeset" : ["padID", "rev"]
|
||||
, "getLastEdited" : ["padID"]
|
||||
, "deletePad" : ["padID"]
|
||||
, "copyPad" : ["sourceID", "destinationID", "force"]
|
||||
, "movePad" : ["sourceID", "destinationID", "force"]
|
||||
, "getReadOnlyID" : ["padID"]
|
||||
, "getPadID" : ["roID"]
|
||||
, "setPublicStatus" : ["padID", "publicStatus"]
|
||||
, "getPublicStatus" : ["padID"]
|
||||
, "setPassword" : ["padID", "password"]
|
||||
, "isPasswordProtected" : ["padID"]
|
||||
, "listAuthorsOfPad" : ["padID"]
|
||||
, "padUsersCount" : ["padID"]
|
||||
, "getAuthorName" : ["authorID"]
|
||||
, "padUsers" : ["padID"]
|
||||
, "sendClientsMessage" : ["padID", "msg"]
|
||||
, "listAllGroups" : []
|
||||
, "checkToken" : []
|
||||
, "appendChatMessage" : ["padID", "text", "authorID", "time"]
|
||||
, "getChatHistory" : ["padID"]
|
||||
, "getChatHistory" : ["padID", "start", "end"]
|
||||
, "getChatHead" : ["padID"]
|
||||
, "restoreRevision" : ["padID", "rev"]
|
||||
, "appendText" : ["padID", "text"]
|
||||
}
|
||||
};
|
||||
|
||||
// set the latest available API version here
|
||||
exports.latestApiVersion = '1.2.12';
|
||||
exports.latestApiVersion = '1.2.13';
|
||||
|
||||
// exports the versions so it can be used by the new Swagger endpoint
|
||||
exports.version = version;
|
||||
|
|
|
@ -30,9 +30,15 @@ var os = require('os');
|
|||
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||
var TidyHtml = require('../utils/TidyHtml');
|
||||
|
||||
var convertor = null;
|
||||
|
||||
//load abiword only if its enabled
|
||||
if(settings.abiword != null)
|
||||
var abiword = require("../utils/Abiword");
|
||||
convertor = require("../utils/Abiword");
|
||||
|
||||
// Use LibreOffice if an executable has been defined in the settings
|
||||
if(settings.soffice != null)
|
||||
convertor = require("../utils/LibreOffice");
|
||||
|
||||
var tempDirectory = "/tmp";
|
||||
|
||||
|
@ -70,71 +76,11 @@ exports.doExport = function(req, res, padId, type)
|
|||
}
|
||||
else if(type == "txt")
|
||||
{
|
||||
var txt;
|
||||
var randNum;
|
||||
var srcFile, destFile;
|
||||
|
||||
async.series([
|
||||
//render the txt document
|
||||
function(callback)
|
||||
{
|
||||
exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, _txt)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
txt = _txt;
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//decide what to do with the txt export
|
||||
function(callback)
|
||||
{
|
||||
//if this is a txt export, we can send this from here directly
|
||||
res.send(txt);
|
||||
callback("stop");
|
||||
},
|
||||
//send the convert job to abiword
|
||||
function(callback)
|
||||
{
|
||||
//ensure html can be collected by the garbage collector
|
||||
txt = null;
|
||||
|
||||
destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type;
|
||||
abiword.convertFile(srcFile, destFile, type, callback);
|
||||
},
|
||||
//send the file
|
||||
function(callback)
|
||||
{
|
||||
res.sendFile(destFile, null, callback);
|
||||
},
|
||||
//clean up temporary files
|
||||
function(callback)
|
||||
{
|
||||
async.parallel([
|
||||
function(callback)
|
||||
{
|
||||
fs.unlink(srcFile, callback);
|
||||
},
|
||||
function(callback)
|
||||
{
|
||||
//100ms delay to accomidate for slow windows fs
|
||||
if(os.type().indexOf("Windows") > -1)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.unlink(destFile, callback);
|
||||
}
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
], function(err)
|
||||
exporttxt.getPadTXTDocument(padId, req.params.rev, false, function(err, txt)
|
||||
{
|
||||
if(err && err != "stop") ERR(err);
|
||||
})
|
||||
if(ERR(err)) return;
|
||||
res.send(txt);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,11 +129,11 @@ exports.doExport = function(req, res, padId, type)
|
|||
TidyHtml.tidy(srcFile, callback);
|
||||
},
|
||||
|
||||
//send the convert job to abiword
|
||||
//send the convert job to the convertor (abiword, libreoffice, ..)
|
||||
function(callback)
|
||||
{
|
||||
destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type;
|
||||
abiword.convertFile(srcFile, destFile, type, callback);
|
||||
convertor.convertFile(srcFile, destFile, type, callback);
|
||||
},
|
||||
//send the file
|
||||
function(callback)
|
||||
|
|
|
@ -1020,6 +1020,8 @@ function handleClientReady(client, message)
|
|||
var currentTime;
|
||||
var padIds;
|
||||
|
||||
hooks.callAll("clientReady", message);
|
||||
|
||||
async.series([
|
||||
//Get ro/rw id:s
|
||||
function (callback)
|
||||
|
@ -1229,6 +1231,7 @@ function handleClientReady(client, message)
|
|||
"plugins": plugins.plugins,
|
||||
"parts": plugins.parts,
|
||||
},
|
||||
"indentationOnNewLine": settings.indentationOnNewLine,
|
||||
"initialChangesets": [] // FIXME: REMOVE THIS SHIT
|
||||
}
|
||||
|
||||
|
@ -1365,6 +1368,12 @@ function handleChangesetRequest(client, message)
|
|||
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||
return;
|
||||
}
|
||||
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill
|
||||
if(Math.floor(message.data.granularity) !== message.data.granularity)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request granularity is not an integer!");
|
||||
return;
|
||||
}
|
||||
if(message.data.start == null)
|
||||
{
|
||||
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||
|
|
|
@ -16,6 +16,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
if(sanitizedPadId != padId)
|
||||
{
|
||||
var real_url = sanitizedPadId;
|
||||
real_url = encodeURIComponent(real_url);
|
||||
var query = url.parse(req.url).query;
|
||||
if ( query ) real_url += '?' + query;
|
||||
res.header('Location', real_url);
|
||||
|
|
|
@ -19,6 +19,7 @@ var async = require("async");
|
|||
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
|
||||
var padManager = require("../db/PadManager");
|
||||
var ERR = require("async-stacktrace");
|
||||
var _ = require('underscore');
|
||||
var Security = require('ep_etherpad-lite/static/js/security');
|
||||
var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
var _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
|
@ -77,12 +78,21 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
var tags = ['h1', 'h2', 'strong', 'em', 'u', 's'];
|
||||
var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
|
||||
|
||||
// prepare tags stored as ['tag', true] to be exported
|
||||
hooks.aCallAll("exportHtmlAdditionalTags", pad, function(err, newProps){
|
||||
newProps.forEach(function (propName, i){
|
||||
tags.push(propName);
|
||||
props.push(propName);
|
||||
});
|
||||
});
|
||||
// prepare tags stored as ['tag', 'value'] to be exported. This will generate HTML
|
||||
// with tags like <span data-tag="value">
|
||||
hooks.aCallAll("exportHtmlAdditionalTagsWithData", pad, function(err, newProps){
|
||||
newProps.forEach(function (propName, i){
|
||||
tags.push('span data-' + propName[0] + '="' + propName[1] + '"');
|
||||
props.push(propName);
|
||||
});
|
||||
});
|
||||
|
||||
// holds a map of used styling attributes (*1, *2, etc) in the apool
|
||||
// and maps them to an index in props
|
||||
|
@ -130,7 +140,13 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
// this pad, and if yes puts its attrib id->props value into anumMap
|
||||
props.forEach(function (propName, i)
|
||||
{
|
||||
var propTrueNum = apool.putAttrib([propName, true], true);
|
||||
var attrib = [propName, true];
|
||||
if (_.isArray(propName)) {
|
||||
// propName can be in the form of ['color', 'red'],
|
||||
// see hook exportHtmlAdditionalTagsWithData
|
||||
attrib = propName;
|
||||
}
|
||||
var propTrueNum = apool.putAttrib(attrib, true);
|
||||
if (propTrueNum >= 0)
|
||||
{
|
||||
anumMap[propTrueNum] = i;
|
||||
|
@ -154,6 +170,12 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
|
||||
var property = props[i];
|
||||
|
||||
// we are not insterested on properties in the form of ['color', 'red'],
|
||||
// see hook exportHtmlAdditionalTagsWithData
|
||||
if (_.isArray(property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(property.substr(0,6) === "author"){
|
||||
return stripDotFromAuthorID(property);
|
||||
}
|
||||
|
@ -165,6 +187,13 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
return false;
|
||||
}
|
||||
|
||||
// tags added by exportHtmlAdditionalTagsWithData will be exported as <span> with
|
||||
// data attributes
|
||||
function isSpanWithData(i){
|
||||
var property = props[i];
|
||||
return _.isArray(property);
|
||||
}
|
||||
|
||||
function emitOpenTag(i)
|
||||
{
|
||||
openTags.unshift(i);
|
||||
|
@ -186,8 +215,9 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
{
|
||||
openTags.shift();
|
||||
var spanClass = getSpanClassFor(i);
|
||||
var spanWithData = isSpanWithData(i);
|
||||
|
||||
if(spanClass){
|
||||
if(spanClass || spanWithData){
|
||||
assem.append('</span>');
|
||||
} else {
|
||||
assem.append('</');
|
||||
|
@ -464,6 +494,9 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
|||
(noDocType ? '' : '<!doctype html>\n') +
|
||||
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
|
||||
'<title>' + Security.escapeHTML(padId) + '</title>\n' +
|
||||
'<meta name="generator" content="Etherpad">\n' +
|
||||
'<meta name="author" content="Etherpad">\n' +
|
||||
'<meta name="changedby" content="Etherpad">\n' +
|
||||
'<meta charset="utf-8">\n' +
|
||||
'<style> * { font-family: arial, sans-serif;\n' +
|
||||
'font-size: 13px;\n' +
|
||||
|
|
93
src/node/utils/LibreOffice.js
Normal file
93
src/node/utils/LibreOffice.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Controls the communication with LibreOffice
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var async = require("async");
|
||||
var fs = require("fs");
|
||||
var os = require("os");
|
||||
var path = require("path");
|
||||
var settings = require("./Settings");
|
||||
var spawn = require("child_process").spawn;
|
||||
|
||||
// Conversion tasks will be queued up, so we don't overload the system
|
||||
var queue = async.queue(doConvertTask, 1);
|
||||
|
||||
/**
|
||||
* Convert a file from one type to another
|
||||
*
|
||||
* @param {String} srcFile The path on disk to convert
|
||||
* @param {String} destFile The path on disk where the converted file should be stored
|
||||
* @param {String} type The type to convert into
|
||||
* @param {Function} callback Standard callback function
|
||||
*/
|
||||
exports.convertFile = function(srcFile, destFile, type, callback) {
|
||||
queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback});
|
||||
};
|
||||
|
||||
function doConvertTask(task, callback) {
|
||||
var tmpDir = os.tmpdir();
|
||||
|
||||
async.series([
|
||||
// Generate a PDF file with LibreOffice
|
||||
function(callback) {
|
||||
var soffice = spawn(settings.soffice, [
|
||||
'--headless',
|
||||
'--invisible',
|
||||
'--nologo',
|
||||
'--nolockcheck',
|
||||
'--convert-to', task.type,
|
||||
task.srcFile,
|
||||
'--outdir', tmpDir
|
||||
]);
|
||||
|
||||
var stdoutBuffer = '';
|
||||
|
||||
// Delegate the processing of stdout to another function
|
||||
soffice.stdout.on('data', function(data) {
|
||||
stdoutBuffer += data.toString();
|
||||
});
|
||||
|
||||
// Append error messages to the buffer
|
||||
soffice.stderr.on('data', function(data) {
|
||||
stdoutBuffer += data.toString();
|
||||
});
|
||||
|
||||
// Throw an exception if libreoffice failed
|
||||
soffice.on('exit', function(code) {
|
||||
if (code != 0) {
|
||||
return callback("LibreOffice died with exit code " + code + " and message: " + stdoutBuffer);
|
||||
}
|
||||
|
||||
callback();
|
||||
})
|
||||
},
|
||||
|
||||
// Move the PDF file to the correct place
|
||||
function(callback) {
|
||||
var filename = path.basename(task.srcFile);
|
||||
var pdfFilename = filename.substr(0, filename.lastIndexOf('.')) + '.' + task.type;
|
||||
var pdfPath = path.join(tmpDir, pdfFilename);
|
||||
fs.rename(pdfPath, task.destFile, callback);
|
||||
}
|
||||
], function(err) {
|
||||
// Invoke the callback for the local queue
|
||||
callback();
|
||||
|
||||
// Invoke the callback for the task
|
||||
task.callback(err);
|
||||
});
|
||||
}
|
|
@ -152,6 +152,11 @@ exports.minify = true;
|
|||
*/
|
||||
exports.abiword = null;
|
||||
|
||||
/**
|
||||
* The path of the libreoffice executable
|
||||
*/
|
||||
exports.soffice = null;
|
||||
|
||||
/**
|
||||
* The path of the tidy executable
|
||||
*/
|
||||
|
@ -177,6 +182,11 @@ exports.disableIPlogging = false;
|
|||
*/
|
||||
exports.loadTest = false;
|
||||
|
||||
/**
|
||||
* Enable indentation on new lines
|
||||
*/
|
||||
exports.indentationOnNewLine = true;
|
||||
|
||||
/*
|
||||
* log4js appender configuration
|
||||
*/
|
||||
|
@ -218,8 +228,14 @@ exports.getGitCommit = function() {
|
|||
try
|
||||
{
|
||||
var rootPath = path.resolve(npm.dir, '..');
|
||||
var ref = fs.readFileSync(rootPath + "/.git/HEAD", "utf-8");
|
||||
var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
|
||||
if (fs.lstatSync(rootPath + '/.git').isFile()) {
|
||||
rootPath = fs.readFileSync(rootPath + '/.git', "utf8");
|
||||
rootPath = rootPath.split(' ').pop().trim();
|
||||
} else {
|
||||
rootPath += '/.git';
|
||||
}
|
||||
var ref = fs.readFileSync(rootPath + "/HEAD", "utf-8");
|
||||
var refPath = rootPath + "/" + ref.substring(5, ref.indexOf("\n"));
|
||||
version = fs.readFileSync(refPath, "utf-8");
|
||||
version = version.substring(0, 7);
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ plugins.ensure(function () {\n\
|
|||
iframeHTML: iframeHTML
|
||||
});
|
||||
|
||||
iframeHTML.push('</head><body id="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>');
|
||||
iframeHTML.push('</head><body id="innerdocbody" class="innerdocbody" role="application" class="syntax" spellcheck="false"> </body></html>');
|
||||
|
||||
// Expose myself to global for my child frame.
|
||||
var thisFunctionsName = "ChildAccessibleAce2Editor";
|
||||
|
@ -315,7 +315,7 @@ window.onload = function () {\n\
|
|||
|
||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||
// (throbs busy while typing)
|
||||
outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>');
|
||||
outerHTML.push('<style type="text/css" title="dynamicsyntax"></style>', '<link rel="stylesheet" type="text/css" href="data:text/css,"/>', scriptTag(outerScript), '</head><body id="outerdocbody" class="outerdocbody"><div id="sidediv" class="sidediv"><!-- --></div><div id="linemetricsdiv">x</div></body></html>');
|
||||
|
||||
var outerFrame = document.createElement("IFRAME");
|
||||
outerFrame.name = "ace_outer";
|
||||
|
|
|
@ -1894,7 +1894,11 @@ function Ace2Inner(){
|
|||
var prevLine = rep.lines.prev(thisLine);
|
||||
var prevLineText = prevLine.text;
|
||||
var theIndent = /^ *(?:)/.exec(prevLineText)[0];
|
||||
if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB;
|
||||
var shouldIndent = parent.parent.clientVars.indentationOnNewLine;
|
||||
if (shouldIndent && /[\[\(\:\{]\s*$/.exec(prevLineText))
|
||||
{
|
||||
theIndent += THE_TAB;
|
||||
}
|
||||
var cs = Changeset.builder(rep.lines.totalWidth()).keep(
|
||||
rep.lines.offsetOfIndex(lineNum), lineNum).insert(
|
||||
theIndent, [
|
||||
|
@ -2897,6 +2901,12 @@ function Ace2Inner(){
|
|||
rep.selFocusAtStart = newSelFocusAtStart;
|
||||
currentCallStack.repChanged = true;
|
||||
|
||||
hooks.callAll('aceSelectionChanged', {
|
||||
rep: rep,
|
||||
callstack: currentCallStack,
|
||||
documentAttributeManager: documentAttributeManager,
|
||||
});
|
||||
|
||||
return true;
|
||||
//console.log("selStart: %o, selEnd: %o, focusAtStart: %s", rep.selStart, rep.selEnd,
|
||||
//String(!!rep.selFocusAtStart));
|
||||
|
@ -3632,12 +3642,6 @@ function Ace2Inner(){
|
|||
var altKey = evt.altKey;
|
||||
var shiftKey = evt.shiftKey;
|
||||
|
||||
// prevent ESC key
|
||||
if (keyCode == 27)
|
||||
{
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
// Is caret potentially hidden by the chat button?
|
||||
var myselection = document.getSelection(); // get the current caret selection
|
||||
var caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214
|
||||
|
@ -3706,7 +3710,12 @@ function Ace2Inner(){
|
|||
documentAttributeManager: documentAttributeManager,
|
||||
evt:evt
|
||||
});
|
||||
specialHandled = (specialHandledInHook&&specialHandledInHook.length>0)?specialHandledInHook[0]:specialHandled;
|
||||
|
||||
// if any hook returned true, set specialHandled with true
|
||||
if (specialHandledInHook) {
|
||||
specialHandled = _.contains(specialHandledInHook, true);
|
||||
}
|
||||
|
||||
if ((!specialHandled) && altKey && isTypeForSpecialKey && keyCode == 120){
|
||||
// Alt F9 focuses on the File Menu and/or editbar.
|
||||
// Note that while most editors use Alt F10 this is not desirable
|
||||
|
@ -3830,6 +3839,15 @@ function Ace2Inner(){
|
|||
}, 0);
|
||||
specialHandled = true;
|
||||
}
|
||||
if ((!specialHandled) && isTypeForSpecialKey && keyCode == 27)
|
||||
{
|
||||
// prevent esc key;
|
||||
// in mozilla versions 14-19 avoid reconnecting pad.
|
||||
|
||||
fastIncorp(4);
|
||||
evt.preventDefault();
|
||||
specialHandled = true;
|
||||
}
|
||||
if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "s" && (evt.metaKey || evt.ctrlKey) && !evt.altKey) /* Do a saved revision on ctrl S */
|
||||
{
|
||||
evt.preventDefault();
|
||||
|
@ -4978,6 +4996,13 @@ function Ace2Inner(){
|
|||
if(e.target.a || e.target.localName === "a"){
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Call paste hook
|
||||
hooks.callAll('acePaste', {
|
||||
editorInfo: editorInfo,
|
||||
rep: rep,
|
||||
documentAttributeManager: documentAttributeManager
|
||||
});
|
||||
})
|
||||
|
||||
// CompositionEvent is not implemented below IE version 8
|
||||
|
@ -5347,8 +5372,9 @@ function Ace2Inner(){
|
|||
function initLineNumbers()
|
||||
{
|
||||
lineNumbersShown = 1;
|
||||
sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner"><div>1</div></td></tr></table>';
|
||||
sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right"><tr><td id="sidedivinner" class="sidedivinner"><div>1</div></td></tr></table>';
|
||||
sideDivInner = outerWin.document.getElementById("sidedivinner");
|
||||
$(sideDiv).addClass("sidediv");
|
||||
}
|
||||
|
||||
function updateLineNumbers()
|
||||
|
|
|
@ -11,7 +11,7 @@ $(document).ready(function () {
|
|||
|
||||
//connect
|
||||
var room = url + "pluginfw/installer";
|
||||
socket = io.connect(room, {resource : resource});
|
||||
socket = io.connect(room, {path: baseURL + "socket.io", resource : resource});
|
||||
|
||||
function search(searchTerm, limit) {
|
||||
if(search.searchTerm != searchTerm) {
|
||||
|
|
|
@ -10,7 +10,7 @@ $(document).ready(function () {
|
|||
|
||||
//connect
|
||||
var room = url + "settings";
|
||||
socket = io.connect(room, {resource : resource});
|
||||
socket = io.connect(room, {path: baseURL + "socket.io", resource : resource});
|
||||
|
||||
socket.on('settings', function (settings) {
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
|||
function textify(str)
|
||||
{
|
||||
return sanitizeUnicode(
|
||||
str.replace(/\n/g, '').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
}
|
||||
|
||||
function getAssoc(node, name)
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
</div>
|
||||
|
||||
<div id="editorcontainerbox">
|
||||
<div id="editorcontainer"></div>
|
||||
<div id="editorcontainer" class="editorcontainer"></div>
|
||||
<div id="editorloadingbox">
|
||||
<div id="passwordRequired">
|
||||
<p data-l10n-id="pad.passwordRequired">You need a password to access this pad</p>
|
||||
|
|
|
@ -79,6 +79,8 @@ describe('Permission', function(){
|
|||
-> movePad(newPadID, originalPadId) -- Should provide consistant pad data
|
||||
-> getText(originalPadId) -- Should be "hello world"
|
||||
-> getLastEdited(padID) -- Should not be 0
|
||||
-> appendText(padID, "hello")
|
||||
-> getText(padID) -- Should be "hello worldhello"
|
||||
-> setHTML(padID) -- Should fail on invalid HTML
|
||||
-> setHTML(padID) *3 -- Should fail on invalid HTML
|
||||
-> getHTML(padID) -- Should return HTML close to posted HTML
|
||||
|
@ -483,6 +485,30 @@ describe('getLastEdited', function(){
|
|||
});
|
||||
})
|
||||
|
||||
describe('appendText', function(){
|
||||
it('Append text to a pad Id', function(done) {
|
||||
api.get(endPoint('appendText', '1.2.13')+"&padID="+testPadId+"&text=hello")
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Append Text failed");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getText', function(){
|
||||
it('Gets text on a pad Id', function(done) {
|
||||
api.get(endPoint('getText')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Get Text failed");
|
||||
if(res.body.data.text !== text+"\nhello") throw new Error("Pad Text not set properly");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('setHTML', function(){
|
||||
it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) {
|
||||
var html = "<div><b>Hello HTML</title></head></div>";
|
||||
|
@ -542,8 +568,9 @@ describe('createPad', function(){
|
|||
|
||||
*/
|
||||
|
||||
var endPoint = function(point){
|
||||
return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey;
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
function makeid()
|
||||
|
|
|
@ -68,6 +68,80 @@ describe("indentation button", function(){
|
|||
});
|
||||
});
|
||||
|
||||
it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
//type a bit, make a line break and type again
|
||||
var $firstTextElement = inner$("div").first();
|
||||
$firstTextElement.sendkeys("line with ':'{enter}");
|
||||
$firstTextElement.sendkeys("line with '['{enter}");
|
||||
$firstTextElement.sendkeys("line with '('{enter}");
|
||||
$firstTextElement.sendkeys("line with '{{}'{enter}");
|
||||
|
||||
helper.waitFor(function(){
|
||||
// wait for Etherpad to split four lines into separated divs
|
||||
var $fourthLine = inner$("div").first().next().next().next();
|
||||
return $fourthLine.text().indexOf("line with '{'") === 0;
|
||||
}).done(function(){
|
||||
// we validate bottom to top for easier implementation
|
||||
|
||||
// curly braces
|
||||
var $lineWithCurlyBraces = inner$("div").first().next().next().next();
|
||||
$lineWithCurlyBraces.sendkeys('{{}');
|
||||
pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly
|
||||
var $lineAfterCurlyBraces = inner$("div").first().next().next().next().next();
|
||||
expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces
|
||||
|
||||
// parenthesis
|
||||
var $lineWithParenthesis = inner$("div").first().next().next();
|
||||
$lineWithParenthesis.sendkeys('(');
|
||||
pressEnter();
|
||||
var $lineAfterParenthesis = inner$("div").first().next().next().next();
|
||||
expect($lineAfterParenthesis.text()).to.match(/\s{4}/);
|
||||
|
||||
// bracket
|
||||
var $lineWithBracket = inner$("div").first().next();
|
||||
$lineWithBracket.sendkeys('[');
|
||||
pressEnter();
|
||||
var $lineAfterBracket = inner$("div").first().next().next();
|
||||
expect($lineAfterBracket.text()).to.match(/\s{4}/);
|
||||
|
||||
// colon
|
||||
var $lineWithColon = inner$("div").first();
|
||||
$lineWithColon.sendkeys(':');
|
||||
pressEnter();
|
||||
var $lineAfterColon = inner$("div").first().next();
|
||||
expect($lineAfterColon.text()).to.match(/\s{4}/);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
|
||||
//type a bit, make a line break and type again
|
||||
var $firstTextElement = inner$("div").first();
|
||||
$firstTextElement.sendkeys(" line with some indentation and ':'{enter}");
|
||||
$firstTextElement.sendkeys("line 2{enter}");
|
||||
|
||||
helper.waitFor(function(){
|
||||
// wait for Etherpad to split two lines into separated divs
|
||||
var $secondLine = inner$("div").first().next();
|
||||
return $secondLine.text().indexOf("line 2") === 0;
|
||||
}).done(function(){
|
||||
var $lineWithColon = inner$("div").first();
|
||||
$lineWithColon.sendkeys(':');
|
||||
pressEnter();
|
||||
var $lineAfterColon = inner$("div").first().next();
|
||||
expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces)
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
it("makes text indented and outdented", function() {
|
||||
|
@ -203,3 +277,15 @@ describe("indentation button", function(){
|
|||
});*/
|
||||
|
||||
});
|
||||
|
||||
function pressEnter(){
|
||||
var inner$ = helper.padInner$;
|
||||
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
|
||||
var evtType = "keypress";
|
||||
}else{
|
||||
var evtType = "keydown";
|
||||
}
|
||||
var e = inner$.Event(evtType);
|
||||
e.keyCode = 13; // enter :|
|
||||
inner$("#innerdocbody").trigger(e);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue