Commit b95395a1 authored by John McLear's avatar John McLear
Browse files

Merge pull request #2590 from ether/release/1.5.3

Release/1.5.3
parents b9cdf435 d47e2012
# 1.5.3
* NEW: Accessibility support for Screen readers, includes new fonts and keyboard shortcuts
* NEW: API endpoint for Append Chat Message and Chat Backend Tests
* NEW: Error messages displayed on load are included in Default Pad Text (can be supressed)
* NEW: Content Collector can handle key values
* NEW: getAttributesOnPosition Method
* FIX: Firefox keeps attributes (bold etc) on cut/copy -> paste
* Fix: showControls=false now works
* Fix: Cut and Paste works...
* SECURITY: Don't allow read files on directory traversal
# 1.5.2
* NEW: Support for node version 0.12.x
* NEW: API endpoint saveRevision, getSavedRevisionCount and listSavedRevisions
......
......@@ -46,6 +46,12 @@ Now, run `start.bat` and open <http://localhost:9001> in your browser.
Update to the latest version with `git pull origin`, then run `bin\installOnWindows.bat`, again.
If cloning to a subdirectory within another project, you may need to do the following:
1. Start the server manually (e.g. `node/node_modules/ep_etherpad-lite/node/server.js]`)
2. Edit the db `filename` in `settings.json` to the relative directory with the file (e.g. `application/lib/etherpad-lite/var/dirty.db`)
3. Add auto-generated files to the main project `.gitignore`
[Next steps](#next-steps).
## GNU/Linux and other UNIX-like systems
......
......@@ -103,7 +103,7 @@ if [ $DOWNLOAD_JQUERY = "true" ]; then
fi
#Remove all minified data to force node creating it new
echo "Clear minfified cache..."
echo "Clearing minified cache..."
rm -f var/minified*
echo "Ensure custom css/js files are created..."
......
......@@ -55,7 +55,7 @@ do
TIME_SINCE_LAST_SEND=$(($TIME_NOW - $LAST_EMAIL_SEND))
if [ $TIME_SINCE_LAST_SEND -gt $TIME_BETWEEN_EMAILS ]; then
printf "Server was restared at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 ${LOG})" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS
printf "Server was restarted at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 ${LOG})" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS
LAST_EMAIL_SEND=$TIME_NOW
fi
......
......@@ -203,6 +203,13 @@ Things in context:
This hook is called before the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original.
E.g. if you need to apply an attribute to newly inserted characters,
call cc.doAttrib(state, "attributeName") which results in an attribute attributeName=true.
If you want to specify also a value, call cc.doAttrib(state, "attributeName:value")
which results in an attribute attributeName=value.
## collectContentImage
Called from: src/static/js/contentcollector.js
......
......@@ -54,6 +54,9 @@
//the default text of a pad
"defaultPadText" : "Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at http:\/\/etherpad.org\n",
/* Shoud we suppress errors from being visible in the default Pad Text? */
"suppressErrorsInPadText" : false,
/* Users must have a session to access pads. This effectively allows only group pads to be accessed. */
"requireSession" : false,
......
......@@ -38,7 +38,24 @@
"pad.settings.rtlcheck": "Read content from right to left?",
"pad.settings.fontType": "Font type:",
"pad.settings.fontType.normal": "Normal",
"pad.settings.fontType.opendyslexic": "Open Dyslexic",
"pad.settings.fontType.monospaced": "Monospace",
"pad.settings.fontType.comicsans": "Comic Sans",
"pad.settings.fontType.couriernew": "Courier New",
"pad.settings.fontType.georgia": "Georgia",
"pad.settings.fontType.impact": "Impact",
"pad.settings.fontType.lucida": "Lucida",
"pad.settings.fontType.lucidasans": "Lucida Sans",
"pad.settings.fontType.palatino": "Palatino",
"pad.settings.fontType.tahoma": "Tahoma",
"pad.settings.fontType.timesnewroman": "Times New Roman",
"pad.settings.fontType.trebuchet": "Trebuchet",
"pad.settings.fontType.verdana": "Verdana",
"pad.settings.fontType.symbol": "Symbol",
"pad.settings.fontType.webdings": "Webdings",
"pad.settings.fontType.wingdings": "Wingdings",
"pad.settings.fontType.sansserif": "Sans Serif",
"pad.settings.fontType.serif": "Serif",
"pad.settings.globalView": "Global View",
"pad.settings.language": "Language:",
......@@ -105,6 +122,10 @@
"timeslider.version": "Version {{version}}",
"timeslider.saved": "Saved {{month}} {{day}}, {{year}}",
"timeslider.playPause": "Playback / Pause Pad Contents",
"timeslider.backRevision":"Go back a revision in this Pad",
"timeslider.forwardRevision":"Go forward a revision in this Pad",
"timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
"timeslider.month.january": "January",
"timeslider.month.february": "February",
......
......@@ -432,8 +432,8 @@ getChatHistory(padId, start, end), returns a part of or the whole chat-history o
Example returns:
{"code":0,"message":"ok","data":{"messages":[{"text":"foo","userId":"a.foo","time":1359199533759,"userName":"test"},
{"text":"bar","userId":"a.foo","time":1359199534622,"userName":"test"}]}}
{"code":0,"message":"ok","data":{"messages":[{"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"},
{"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"}]}}
{code: 1, message:"start is higher or equal to the current chatHead", data: null}
......@@ -494,6 +494,33 @@ exports.getChatHistory = function(padID, start, end, callback)
});
}
/**
appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id, time is a timestamp
Example returns:
{code: 0, message:"ok", data: null
{code: 1, message:"padID does not exist", data: null}
*/
exports.appendChatMessage = function(padID, text, authorID, time, 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.appendChatMessage(text, authorID, parseInt(time));
callback();
});
}
/*****************/
/**PAD FUNCTIONS */
/*****************/
......
......@@ -394,10 +394,60 @@ var version =
, "getChatHead" : ["padID"]
, "restoreRevision" : ["padID", "rev"]
}
, "1.2.12":
{ "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"]
}
};
// set the latest available API version here
exports.latestApiVersion = '1.2.11';
exports.latestApiVersion = '1.2.12';
// exports the versions so it can be used by the new Swagger endpoint
exports.version = version;
......
......@@ -148,6 +148,9 @@ exports.doImport = function(req, res, padId)
if(!importHandledByPlugin || !directDatabaseAccess){
var fileEnding = path.extname(srcFile).toLowerCase();
var fileIsHTML = (fileEnding === ".html" || fileEnding === ".htm");
var fileIsTXT = (fileEnding === ".txt");
if (fileIsTXT) abiword = false; // Don't use abiword for text files
// See https://github.com/ether/etherpad-lite/issues/2572
if (abiword && !fileIsHTML) {
abiword.convertFile(srcFile, destFile, "htm", function(err) {
//catch convert errors
......@@ -213,7 +216,7 @@ exports.doImport = function(req, res, padId)
// Title needs to be stripped out else it appends it to the pad..
text = text.replace("<title>", "<!-- <title>");
text = text.replace("</title>","</title>-->");
//node on windows has a delay on releasing of the file lock.
//We add a 100ms delay to work around this
if(os.type().indexOf("Windows") > -1){
......@@ -245,7 +248,6 @@ exports.doImport = function(req, res, padId)
padManager.getPad(padId, function(err, _pad){
var pad = _pad;
padManager.unloadPad(padId);
// direct Database Access means a pad user should perform a switchToPad
// and not attempt to recieve updated pad data..
if(!directDatabaseAccess){
......
......@@ -656,12 +656,17 @@ function handleUserChanges(data, cb)
, op
while(iterator.hasNext()) {
op = iterator.next()
if(op.opcode != '+') continue;
//+ can add text with attribs
//= can change or add attribs
//- can have attribs, but they are discarded and don't show up in the attribs - but do show up in the pool
op.attribs.split('*').forEach(function(attr) {
if(!attr) return
attr = wireApool.getAttrib(attr)
if(!attr) return
if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset);
//the empty author is used in the clearAuthorship functionality so this should be the only exception
if('author' == attr[0] && (attr[1] != thisSession.author && attr[1] != '')) throw new Error("Trying to submit changes as another author in changeset "+changeset);
})
}
......@@ -1629,10 +1634,15 @@ function composePadChangesets(padId, startNum, endNum, callback)
changeset = changesets[startNum];
var pool = pad.apool();
for(var r=startNum+1;r<endNum;r++)
{
var cs = changesets[r];
changeset = Changeset.compose(changeset, cs, pool);
try {
for(var r=startNum+1;r<endNum;r++) {
var cs = changesets[r];
changeset = Changeset.compose(changeset, cs, pool);
}
} catch(e){
// r-1 indicates the rev that was build starting with startNum, applying startNum+1, +2, +3
console.warn("failed to compose cs in pad:",padId," startrev:",startNum," current rev:",r);
return callback(e);
}
callback(null);
......
......@@ -13,6 +13,8 @@ exports.createServer = function () {
console.log("Report bugs at https://github.com/ether/etherpad-lite/issues")
serverName = "Etherpad " + settings.getGitCommit() + " (http://etherpad.org)";
console.log("Your Etherpad version is " + settings.getEpVersion() + " (" + settings.getGitCommit() + ")");
exports.restartServer();
......
......@@ -17,7 +17,13 @@ exports.expressCreateServer = function (hook_name, args, cb) {
});
args.app.get('/admin/plugins/info', function(req, res) {
var gitCommit = settings.getGitCommit();
res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {gitCommit:gitCommit}) );
var epVersion = settings.getEpVersion();
res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html",
{
gitCommit: gitCommit,
epVersion: epVersion
})
);
});
}
......
......@@ -284,6 +284,10 @@ var API = {
}
},
"response": {"chatHead":{"type":"Message"}}
},
"appendChatMessage": {
"func": "appendChatMessage",
"description": "appends a chat message"
}
}
};
......
......@@ -145,7 +145,6 @@ function minify(req, res, next)
filename = path.normalize(path.join(ROOT_DIR, filename));
if (filename.indexOf(ROOT_DIR) == 0) {
filename = filename.slice(ROOT_DIR.length);
filename = filename.replace(/\\/g, '/'); // Windows (safe generally?)
} else {
res.writeHead(404, {});
res.end();
......
......@@ -27,7 +27,7 @@ var npm = require("npm/lib/npm.js");
var jsonminify = require("jsonminify");
var log4js = require("log4js");
var randomString = require("./randomstring");
var suppressDisableMsg = " -- To suppress these warning messages change suppressErrorsInPadText to true in your settings.json\n";
/* Root path of the installation */
exports.root = path.normalize(path.join(npm.dir, ".."));
......@@ -54,6 +54,11 @@ exports.ip = "0.0.0.0";
*/
exports.port = process.env.PORT || 9001;
/**
* Should we suppress Error messages from being in Pad Contents
*/
exports.suppressErrorsInPadText = false;
/**
* The SSL signed server key and the Certificate Authority's own certificate
* default case: ep-lite does *not* use SSL. A signed server key is not required in this case.
......@@ -95,7 +100,7 @@ exports.toolbar = {
["showusers"]
],
timeslider: [
["timeslider_export", "timeslider_returnToPad"]
["timeslider_export", "timeslider_settings", "timeslider_returnToPad"]
]
}
......@@ -194,7 +199,6 @@ exports.getGitCommit = function() {
var refPath = rootPath + "/.git/" + ref.substring(5, ref.indexOf("\n"));
version = fs.readFileSync(refPath, "utf-8");
version = version.substring(0, 7);
console.log("Your Etherpad git version is " + version);
}
catch(e)
{
......@@ -203,6 +207,11 @@ exports.getGitCommit = function() {
return version;
}
// Return etherpad version from package.json
exports.getEpVersion = function() {
return require('ep_etherpad-lite/package.json').version;
}
exports.reloadSettings = function reloadSettings() {
// Discover where the settings file lives
var settingsFilename = argv.settings || "settings.json";
......@@ -266,7 +275,11 @@ exports.reloadSettings = function reloadSettings() {
{
fs.exists(exports.abiword, function(exists) {
if (!exists) {
console.error("Abiword does not exist at this path, check your settings file");
var abiwordError = "Abiword does not exist at this path, check your settings file";
if(!exports.suppressErrorsInPadText){
exports.defaultPadText = exports.defaultPadText + "\nError: " + abiwordError + suppressDisableMsg;
}
console.error(abiwordError);
exports.abiword = null;
}
});
......@@ -275,11 +288,19 @@ exports.reloadSettings = function reloadSettings() {
if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here
exports.sessionKey = randomString(32);
console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts");
var sessionWarning = "You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts";
if(!exports.suppressErrorsInPadText){
exports.defaultPadText = exports.defaultPadText + "\nWarning: " + sessionWarning + suppressDisableMsg;
}
console.warn(sessionWarning);
}
if(exports.dbType === "dirty"){
console.warn("DirtyDB is used. This is fine for testing but not recommended for production.");
var dirtyWarning = "DirtyDB is used. This is fine for testing but not recommended for production.";
if(!exports.suppressErrorsInPadText){
exports.defaultPadText = exports.defaultPadText + "\nWarning: " + dirtyWarning + suppressDisableMsg;
}
console.warn(dirtyWarning);
}
};
......
......@@ -99,12 +99,14 @@ _.extend(Button.prototype, {
};
return tag("li", liAttributes,
tag("a", { "class": this.grouping, "data-l10n-id": this.attributes.localizationId },
tag("span", { "class": " "+ this.attributes.class })
tag("button", { "class": " "+ this.attributes.class, "data-l10n-id": this.attributes.localizationId })
)
);
}
});
SelectButton = function (attributes) {
this.attributes = attributes;
this.options = [];
......@@ -208,6 +210,12 @@ module.exports = {
class: "buttonicon buttonicon-import_export"
},
timeslider_settings: {
command: "settings",
localizationId: "pad.toolbar.settings.title",
class: "buttonicon buttonicon-settings"
},
timeslider_returnToPad: {
command: "timeslider_returnToPad",
localizationId: "timeslider.toolbar.returnbutton",
......
......@@ -13,25 +13,25 @@
],
"dependencies" : {
"etherpad-yajsml" : "0.0.2",
"request" : "2.53.0",
"request" : "2.55.0",
"etherpad-require-kernel" : "1.0.8",
"resolve" : "1.1.0",
"socket.io" : "1.3.3",
"ueberDB" : "0.2.13",
"resolve" : "1.1.6",
"socket.io" : "1.3.5",
"ueberDB" : "0.2.15",
"express" : "3.8.1",
"async" : "0.9.0",
"connect" : "2.7.11",
"clean-css" : "3.0.8",
"uglify-js" : "2.4.16",
"formidable" : "1.0.16",
"clean-css" : "3.1.9",
"uglify-js" : "2.4.19",
"formidable" : "1.0.17",
"log4js" : "0.6.22",
"cheerio" : "0.18.0",
"cheerio" : "0.19.0",
"async-stacktrace" : "0.0.2",
"npm" : "2.4.1",
"npm" : "2.7.5",
"ejs" : "1.0.0",
"graceful-fs" : "3.0.5",
"graceful-fs" : "3.0.6",
"slide" : "1.1.6",
"semver" : "4.2.0",
"semver" : "4.3.3",
"security" : "1.0.0",
"tinycon" : "0.0.1",
"underscore" : "1.5.1",
......@@ -41,7 +41,7 @@
"channels" : "0.0.4",
"jsonminify" : "0.2.3",
"measured" : "1.0.0",
"mocha" : "2.1.0",
"mocha" : "2.2.1",
"supertest" : "0.15.0"
},
"bin": { "etherpad-lite": "./node/server.js" },
......@@ -54,5 +54,5 @@
"repository" : { "type" : "git",
"url" : "http://github.com/ether/etherpad-lite.git"
},
"version" : "1.5.2"
"version" : "1.5.3"
}
......@@ -98,10 +98,26 @@ body.grayedout { background-color: #eee !important }
}
body.doesWrap {
white-space: pre-wrap; /*Must be pre-wrap to keep trailing spaces. Otherwise you get a zombie caret, walking around your screen (see #1766), WARNING: Enabling this causes Paste as plain text in Chrome to remove line breaks, this is probably undesirable */
/* white-space: pre-wrap; */
/*
Must be pre-wrap to keep trailing spaces. Otherwise you get a zombie caret,
walking around your screen (see #1766).
WARNING: Enabling this causes Paste as plain text in Chrome to remove line breaks
this is probably undesirable
WARNING: This causes copy & paste events to lose bold etc. attributes
NOTE: The walking-zombie caret issue seems to have been fixed in FF upstream
so let's try diabling pre-wrap and see how we get on now.
For more details see: https://github.com/ether/etherpad-lite/issues/2574
*/
word-wrap: break-word; /* fix for issue #1648 - firefox not wrapping long lines (without spaces) correctly */
}
body.doesWrap > div{
/* Related to #1766 */
white-space: pre-wrap;
}
#innerdocbody {
padding-top: 1px; /* important for some reason? */
padding-right: 10px;
......
......@@ -70,10 +70,6 @@ a img {
.toolbar ul li {
float: left;
margin-left: 2px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
height:32px;
}
.toolbar ul li.separator {
......@@ -141,9 +137,24 @@ a img {
top: 1px;
}
.toolbar ul li a .buttontext {
color: #222;
color: #666;
font-size: 14px;
border:none;
background:none;
margin-top:1px;
color:#666;
}
.buttontext::-moz-focus-inner {
padding: 0;
border: 0;
}
.buttontext:focus{
/* Not sure why important is required here but it is */
border: 1px solid #666 !important;
}
.toolbar ul li a.grouped-left {
border-radius: 3px 0 0 3px;
}
......@@ -197,6 +208,7 @@ li[data-key=showusers] > a #online_count {
#editbar{
display:none;
}
#editorcontainer {
position: absolute;
top: 37px; /* + 1px border */
......@@ -742,12 +754,24 @@ table#otheruserstable {
height: 16px;
display: inline-block;
vertical-align: middle;
border: none;
padding: 0;
background: none;
font-family: "fontawesome-etherpad";
font-size: 15px;
font-style: normal;
font-weight: normal;
color: #666;
cursor: pointer;
}
.buttonicon::-moz-focus-inner {
padding: 0;
border: 0
}
.buttonicon:focus{
border: 1px solid #666;
}
.buttonicon-bold:before {
content: "\e81c";
......@@ -1216,6 +1240,11 @@ input[type=checkbox] {
}
/* End of gritter stuff */
@font-face {
font-family: opendyslexic;
src: url("../../static/font/opendyslexic.otf") format("opentype");
}
@font-face {
font-family: "fontawesome-etherpad";
src:url("../font/fontawesome-etherpad.eot");
......@@ -1254,3 +1283,11 @@ input[type=checkbox] {
-moz-osx-font-smoothing: grayscale;
}