From 3ab489e92d9e073036f36182a30545628cd2e227 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Feb 2017 09:09:47 +0100 Subject: [PATCH 01/25] added test for selectText function, but discovered that this can't be tested at the moment without a browser, due to jsdom lacking getSelect support --- js/test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/js/test.js b/js/test.js index b2d5f005..d9eb8703 100644 --- a/js/test.js +++ b/js/test.js @@ -66,6 +66,33 @@ describe('helper', function () { }); }); + // this test is not yet meaningful using jsdom, as it does not contain getSelection support. + // TODO: This needs to be tested using a browser. + describe('selectText', function () { + jsc.property( + 'selection contains content of given ID', + 'nearray string', + 'nearray nestring', + function (ids, contents) { + //console.log(ids, contents); + var html = '', + result = true; + ids.forEach(function(item, i) { + html += '
' + (contents[i] || contents[0]) + '
'; + }); + var clean = jsdom(html); + ids.forEach(function(item, i) { + $.PrivateBin.helper.selectText(item); + // TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet. + // Once there is one, uncomment the line below to actually check the result. + //result *= (contents[i] || contents[0]) === window.getSelection().toString(); + }); + clean(); + return result; + } + ); + }); + describe('scriptLocation', function () { jsc.property( 'returns the URL without query & fragment', From 61a59911b8bf3f0e5aa647d6b870e8c9ebfbc2db Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Feb 2017 09:56:56 +0100 Subject: [PATCH 02/25] added test for setElementText function --- js/test.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/js/test.js b/js/test.js index d9eb8703..93e7a8c1 100644 --- a/js/test.js +++ b/js/test.js @@ -71,24 +71,48 @@ describe('helper', function () { describe('selectText', function () { jsc.property( 'selection contains content of given ID', + jsc.nearray(jsc.nearray(jsc.elements(alnumString))), 'nearray string', - 'nearray nestring', function (ids, contents) { - //console.log(ids, contents); var html = '', result = true; ids.forEach(function(item, i) { - html += '
' + (contents[i] || contents[0]) + '
'; + html += '
' + (contents[i] || contents[0]) + '
'; }); var clean = jsdom(html); ids.forEach(function(item, i) { - $.PrivateBin.helper.selectText(item); + $.PrivateBin.helper.selectText(item.join('')); // TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet. // Once there is one, uncomment the line below to actually check the result. //result *= (contents[i] || contents[0]) === window.getSelection().toString(); }); clean(); - return result; + return Boolean(result); + } + ); + }); + + describe('setElementText', function () { + jsc.property( + 'replaces the content of an element', + jsc.nearray(jsc.nearray(jsc.elements(alnumString))), + 'nearray string', + 'string', + function (ids, contents, replacingContent) { + var html = '', + result = true; + ids.forEach(function(item, i) { + html += '
' + (contents[i] || contents[0]) + '
'; + }); + var clean = jsdom(html); + ids.forEach(function(item, i) { + var id = item.join(''), + element = $(document.getElementById(id)); + $.PrivateBin.helper.setElementText(element, replacingContent); + result *= replacingContent === $(document.getElementById(id)).text(); + }); + clean(); + return Boolean(result); } ); }); From b992bcc7322617fd215ce29a3f67283b89a897e5 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Feb 2017 10:40:34 +0100 Subject: [PATCH 03/25] added test for setMessage function, fixing bug for elements with only one child --- js/privatebin.js | 2 +- js/test.js | 52 +++++++++++++++++++++++++++++++++++++++++++++-- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 4fd0e99b..8b98ba8b 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -136,7 +136,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { setMessage: function(element, message) { var content = element.contents(); - if (content.length > 0) + if (content.length > 1) { content[content.length - 1].nodeValue = ' ' + message; } diff --git a/js/test.js b/js/test.js index 93e7a8c1..7906743c 100644 --- a/js/test.js +++ b/js/test.js @@ -77,7 +77,7 @@ describe('helper', function () { var html = '', result = true; ids.forEach(function(item, i) { - html += '
' + (contents[i] || contents[0]) + '
'; + html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; }); var clean = jsdom(html); ids.forEach(function(item, i) { @@ -102,7 +102,7 @@ describe('helper', function () { var html = '', result = true; ids.forEach(function(item, i) { - html += '
' + (contents[i] || contents[0]) + '
'; + html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; }); var clean = jsdom(html); ids.forEach(function(item, i) { @@ -117,6 +117,54 @@ describe('helper', function () { ); }); + describe('setMessage', function () { + jsc.property( + 'replaces the content of an empty element, analog to setElementText', + jsc.nearray(jsc.nearray(jsc.elements(alnumString))), + 'nearray string', + 'string', + function (ids, contents, replacingContent) { + var html = '', + result = true; + ids.forEach(function(item, i) { + html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; + }); + var clean = jsdom(html); + ids.forEach(function(item, i) { + var id = item.join(''), + element = $(document.getElementById(id)); + $.PrivateBin.helper.setMessage(element, replacingContent); + result *= replacingContent === $(document.getElementById(id)).text(); + }); + clean(); + return Boolean(result); + } + ); + jsc.property( + 'replaces the last child of a non-empty element', + jsc.nearray(jsc.nearray(jsc.elements(alnumString))), + jsc.nearray(jsc.nearray(jsc.elements(alnumString))), + 'nearray string', + 'string', + function (ids, classes, contents, replacingContent) { + var html = '', + result = true; + ids.forEach(function(item, i) { + html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; + }); + var clean = jsdom(html); + ids.forEach(function(item, i) { + var id = item.join(''), + element = $(document.getElementById(id)); + $.PrivateBin.helper.setMessage(element, replacingContent); + result *= ' ' + replacingContent === $(document.getElementById(id)).contents()[1].nodeValue; + }); + clean(); + return Boolean(result); + } + ); + }); + describe('scriptLocation', function () { jsc.property( 'returns the URL without query & fragment', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 60c6727d..50f203bd 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index c92136f3..88af3aa9 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From b00bcd1352e1726a3f48309200b79bd9841084a4 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Feb 2017 16:02:24 +0100 Subject: [PATCH 04/25] added test for urls2links function, fixing bug - asterisk is allowed in URLs query string --- js/privatebin.js | 4 ++-- js/test.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 8b98ba8b..e5067f47 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -164,13 +164,13 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var markup = '$1'; element.html( element.html().replace( - /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+-]+(?![\w\s?&.\/;#~%"=-]*>))/ig, + /((http|https|ftp):\/\/[\w?=&.\/-;#@~%+*-]+(?![\w\s?&.\/;#~%"=-]*>))/ig, markup ) ); element.html( element.html().replace( - /((magnet):[\w?=&.\/-;#@~%+-]+)/ig, + /((magnet):[\w?=&.\/-;#@~%+*-]+)/ig, markup ) ); diff --git a/js/test.js b/js/test.js index 7906743c..09ba8813 100644 --- a/js/test.js +++ b/js/test.js @@ -165,6 +165,64 @@ describe('helper', function () { ); }); + describe('urls2links', function () { + jsc.property( + 'ignores non-URL content', + 'string', + function (content) { + var element = $('
' + content + '
'), + before = element.html(); + $.PrivateBin.helper.urls2links(element); + return before === element.html(); + } + ); + jsc.property( + 'replaces URLs with anchors', + 'string', + jsc.elements(['http', 'https', 'ftp']), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + jsc.array(jsc.elements(queryString)), + 'string', + function (prefix, schema, address, query, fragment, postfix) { + var query = query.join(''), + fragment = fragment.join(''), + url = schema + '://' + address.join('') + '/?' + query + '#' + fragment, + prefix = $.PrivateBin.helper.htmlEntities(prefix), + postfix = ' ' + $.PrivateBin.helper.htmlEntities(postfix), + element = $('
' + prefix + url + postfix + '
'); + + // special cases: When the query string and fragment imply the beginning of an HTML entity, eg. � or &#x + if ( + query.slice(-1) === '&' && + (parseInt(fragment.substring(0, 1), 10) >= 0 || fragment.charAt(0) === 'x' ) + ) + { + url = schema + '://' + address.join('') + '/?' + query.substring(0, query.length - 1); + postfix = ''; + element = $('
' + prefix + url + '
'); + } + + $.PrivateBin.helper.urls2links(element); + return element.html() === $('
' + prefix + '' + url + '' + postfix + '
').html(); + } + ); + jsc.property( + 'replaces magnet links with anchors', + 'string', + jsc.array(jsc.elements(queryString)), + 'string', + function (prefix, query, postfix) { + var url = 'magnet:?' + query.join(''), + prefix = $.PrivateBin.helper.htmlEntities(prefix), + postfix = $.PrivateBin.helper.htmlEntities(postfix), + element = $('
' + prefix + url + ' ' + postfix + '
'); + $.PrivateBin.helper.urls2links(element); + return element.html() === $('
' + prefix + '' + url + ' ' + postfix + '
').html(); + } + ); + }); + describe('scriptLocation', function () { jsc.property( 'returns the URL without query & fragment', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 50f203bd..15f83e09 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 88af3aa9..ccfeacb1 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From b9c05b06d0aa318ac8dd49c1c7f405d37abc3135 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Feb 2017 19:34:51 +0100 Subject: [PATCH 05/25] added test for sprintf function, removing dead code and optimizing test cases --- js/privatebin.js | 36 ++++++++-------- js/test.js | 104 ++++++++++++++++++++++++++++++++++++++++------ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 111 insertions(+), 33 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index e5067f47..bb742ea8 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -179,6 +179,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * minimal sprintf emulation for %s and %d formats * + * Note that this function needs the parameters in the same order as the + * format strings appear in the string, contrary to the original. + * * @see {@link https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format#4795914} * @name helper.sprintf * @function @@ -195,27 +198,22 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } var format = args[0], i = 1; - return format.replace(/%((%)|s|d)/g, function (m) { + return format.replace(/%(s|d)/g, function (m) { // m is the matched format, e.g. %s, %d - var val; - if (m[2]) { - val = m[2]; - } else { - val = args[i]; - // A switch statement so that the formatter can be extended. - switch (m) - { - case '%d': - val = parseFloat(val); - if (isNaN(val)) { - val = 0; - } - break; - default: - // Default is %s - } - ++i; + var val = args[i]; + // A switch statement so that the formatter can be extended. + switch (m) + { + case '%d': + val = parseFloat(val); + if (isNaN(val)) { + val = 0; + } + break; + default: + // Default is %s } + ++i; return val; }); }, diff --git a/js/test.js b/js/test.js index 09ba8813..cf44a19c 100644 --- a/js/test.js +++ b/js/test.js @@ -93,6 +93,10 @@ describe('helper', function () { }); describe('setElementText', function () { + after(function () { + cleanup(); + }); + jsc.property( 'replaces the content of an element', jsc.nearray(jsc.nearray(jsc.elements(alnumString))), @@ -104,20 +108,23 @@ describe('helper', function () { ids.forEach(function(item, i) { html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; }); - var clean = jsdom(html); + var elements = $('').html(html); ids.forEach(function(item, i) { var id = item.join(''), - element = $(document.getElementById(id)); + element = elements.find('#' + id).first(); $.PrivateBin.helper.setElementText(element, replacingContent); - result *= replacingContent === $(document.getElementById(id)).text(); + result *= replacingContent === element.text(); }); - clean(); return Boolean(result); } ); }); describe('setMessage', function () { + after(function () { + cleanup(); + }); + jsc.property( 'replaces the content of an empty element, analog to setElementText', jsc.nearray(jsc.nearray(jsc.elements(alnumString))), @@ -129,14 +136,13 @@ describe('helper', function () { ids.forEach(function(item, i) { html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; }); - var clean = jsdom(html); + var elements = $('').html(html); ids.forEach(function(item, i) { var id = item.join(''), - element = $(document.getElementById(id)); + element = elements.find('#' + id).first(); $.PrivateBin.helper.setMessage(element, replacingContent); - result *= replacingContent === $(document.getElementById(id)).text(); + result *= replacingContent === element.text(); }); - clean(); return Boolean(result); } ); @@ -152,20 +158,23 @@ describe('helper', function () { ids.forEach(function(item, i) { html += '
' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '
'; }); - var clean = jsdom(html); + var elements = $('').html(html); ids.forEach(function(item, i) { var id = item.join(''), - element = $(document.getElementById(id)); + element = elements.find('#' + id).first(); $.PrivateBin.helper.setMessage(element, replacingContent); - result *= ' ' + replacingContent === $(document.getElementById(id)).contents()[1].nodeValue; + result *= ' ' + replacingContent === element.contents()[1].nodeValue; }); - clean(); return Boolean(result); } ); }); describe('urls2links', function () { + after(function () { + cleanup(); + }); + jsc.property( 'ignores non-URL content', 'string', @@ -223,6 +232,77 @@ describe('helper', function () { ); }); + describe('sprintf', function () { + after(function () { + cleanup(); + }); + + jsc.property( + 'replaces %s in strings with first given parameter', + 'string', + '(small nearray) string', + 'string', + function (prefix, params, postfix) { + params.unshift(prefix + '%s' + postfix); + var result = prefix + params[1] + postfix; + return result === $.PrivateBin.helper.sprintf.apply(this, params) && + result === $.PrivateBin.helper.sprintf(params); + } + ); + jsc.property( + 'replaces %d in strings with first given parameter', + 'string', + '(small nearray) nat', + 'string', + function (prefix, params, postfix) { + params.unshift(prefix + '%d' + postfix); + var result = prefix + params[1] + postfix; + return result === $.PrivateBin.helper.sprintf.apply(this, params) && + result === $.PrivateBin.helper.sprintf(params); + } + ); + jsc.property( + 'replaces %d in strings with 0 if first parameter is not a number', + 'string', + '(small nearray) falsy', + 'string', + function (prefix, params, postfix) { + params.unshift(prefix + '%d' + postfix); + var result = prefix + '0' + postfix; + return result === $.PrivateBin.helper.sprintf.apply(this, params) && + result === $.PrivateBin.helper.sprintf(params); + } + ); + jsc.property( + 'replaces %d and %s in strings in order', + 'string', + 'nat', + 'string', + 'string', + 'string', + function (prefix, uint, middle, string, postfix) { + var params = [prefix + '%d' + middle + '%s' + postfix, uint, string], + result = prefix + uint + middle + string + postfix; + return result === $.PrivateBin.helper.sprintf.apply(this, params) && + result === $.PrivateBin.helper.sprintf(params); + } + ); + jsc.property( + 'replaces %d and %s in strings in reverse order', + 'string', + 'nat', + 'string', + 'string', + 'string', + function (prefix, uint, middle, string, postfix) { + var params = [prefix + '%s' + middle + '%d' + postfix, string, uint], + result = prefix + string + middle + uint + postfix; + return result === $.PrivateBin.helper.sprintf.apply(this, params) && + result === $.PrivateBin.helper.sprintf(params); + } + ); + }); + describe('scriptLocation', function () { jsc.property( 'returns the URL without query & fragment', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 15f83e09..7f8c25a1 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index ccfeacb1..f2f8ef7a 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From ccac5d373beba7265b261326b50e22ab664f4c1f Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 14:30:41 +0100 Subject: [PATCH 06/25] working on integrating mocha into travis CI --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index c5e0d074..77b6bbd9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,14 @@ php: before_script: - composer install -n + - npm install -g mocha + - cd js + - npm install jsverify jsdom jsdom-global + - cd .. script: - cd tst && phpunit + - cd ../js && mocha after_script: - cd .. From 9d246d4abd8bf66c50aa027325f03a964b996d23 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 14:50:06 +0100 Subject: [PATCH 07/25] trying to convince travis CI to use node.js 4 or later --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 77b6bbd9..d034ad95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ php: - 5.5 - 5.6 - 7.0 +node_js: + - "node" before_script: - composer install -n From 7163ac40b52f6f62b8873e63c673c01114584bc7 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 14:57:20 +0100 Subject: [PATCH 08/25] trying harder to convince travis CI to use node.js 4 --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d034ad95..11575cf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,12 @@ php: - 5.5 - 5.6 - 7.0 -node_js: - - "node" + +env: + - TRAVIS_NODE_VERSION="4" + +install: + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION before_script: - composer install -n From 0a6fd87b4c9541e57dbf36c21b6a1e7170872bae Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 15:02:29 +0100 Subject: [PATCH 09/25] simplify travis CI configuration --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 11575cf2..e8ec3f2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,8 @@ php: - 5.6 - 7.0 -env: - - TRAVIS_NODE_VERSION="4" - install: - - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 4 before_script: - composer install -n From 0c5148a790723cfa63e97ce45909d1dc83d43227 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 15:03:08 +0100 Subject: [PATCH 10/25] updating tested PHP versions in travis CI configuration --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8ec3f2f..0185aa31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: php sudo: false php: - - 5.5 - - 5.6 - - 7.0 + - '5.4' + - '5.5' + - '5.6' + - '7.0' + - '7.1' install: - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 4 From 1457b5ec6ab9ca5fb3564b15af6067c608af8f11 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 15:16:29 +0100 Subject: [PATCH 11/25] ensuring tests will work with PHP 5.4, the currently oldest supported version --- tst/Data/DatabaseTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/Data/DatabaseTest.php b/tst/Data/DatabaseTest.php index 64e9c064..4e06586c 100644 --- a/tst/Data/DatabaseTest.php +++ b/tst/Data/DatabaseTest.php @@ -311,7 +311,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase 'vizhash BLOB, ' . 'postdate INT );' ); - $this->assertInstanceOf(Database::class, Database::getInstance($this->_options)); + $this->assertInstanceOf('PrivateBin\\Data\\Database', Database::getInstance($this->_options)); // check if version number was upgraded in created configuration table $statement = $db->prepare('SELECT value FROM foo_config WHERE id LIKE ?'); From b1396a249d193e1cfe9cb45519e2fefe4694a08b Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 15:30:11 +0100 Subject: [PATCH 12/25] ensuring that in the JS sprintf tests no replacable patterns occur in the pre- & postfix of the test string --- js/test.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/js/test.js b/js/test.js index cf44a19c..b5845ae4 100644 --- a/js/test.js +++ b/js/test.js @@ -243,8 +243,10 @@ describe('helper', function () { '(small nearray) string', 'string', function (prefix, params, postfix) { + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + result = prefix + params[0] + postfix; params.unshift(prefix + '%s' + postfix); - var result = prefix + params[1] + postfix; return result === $.PrivateBin.helper.sprintf.apply(this, params) && result === $.PrivateBin.helper.sprintf(params); } @@ -255,8 +257,10 @@ describe('helper', function () { '(small nearray) nat', 'string', function (prefix, params, postfix) { + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + result = prefix + params[0] + postfix; params.unshift(prefix + '%d' + postfix); - var result = prefix + params[1] + postfix; return result === $.PrivateBin.helper.sprintf.apply(this, params) && result === $.PrivateBin.helper.sprintf(params); } @@ -267,8 +271,10 @@ describe('helper', function () { '(small nearray) falsy', 'string', function (prefix, params, postfix) { + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + result = prefix + '0' + postfix; params.unshift(prefix + '%d' + postfix); - var result = prefix + '0' + postfix; return result === $.PrivateBin.helper.sprintf.apply(this, params) && result === $.PrivateBin.helper.sprintf(params); } @@ -281,7 +287,9 @@ describe('helper', function () { 'string', 'string', function (prefix, uint, middle, string, postfix) { - var params = [prefix + '%d' + middle + '%s' + postfix, uint, string], + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + params = [prefix + '%d' + middle + '%s' + postfix, uint, string], result = prefix + uint + middle + string + postfix; return result === $.PrivateBin.helper.sprintf.apply(this, params) && result === $.PrivateBin.helper.sprintf(params); @@ -295,7 +303,9 @@ describe('helper', function () { 'string', 'string', function (prefix, uint, middle, string, postfix) { - var params = [prefix + '%s' + middle + '%d' + postfix, string, uint], + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + params = [prefix + '%s' + middle + '%d' + postfix, string, uint], result = prefix + string + middle + uint + postfix; return result === $.PrivateBin.helper.sprintf.apply(this, params) && result === $.PrivateBin.helper.sprintf(params); From eedb05111ad9c79aef6f1972c760372c7e3a2a50 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 12 Feb 2017 17:11:21 +0100 Subject: [PATCH 13/25] added test for getCookie function, documenting its limitation of not finding cookies with empty identifier --- js/privatebin.js | 2 +- js/test.js | 27 +++++++++++++++++++++++++++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index bb742ea8..34a00f12 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -224,7 +224,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @see {@link http://www.w3schools.com/js/js_cookies.asp} * @name helper.getCookie * @function - * @param {string} cname + * @param {string} cname - may not be empty * @return {string} */ getCookie: function(cname) { diff --git a/js/test.js b/js/test.js index b5845ae4..68792069 100644 --- a/js/test.js +++ b/js/test.js @@ -313,6 +313,33 @@ describe('helper', function () { ); }); + describe('getCookie', function () { + jsc.property( + 'returns the requested cookie', + 'nearray asciinestring', + 'nearray asciistring', + function (labels, values) { + var selectedKey = '', selectedValue = '', + cookieArray = [], + count = 0; + labels.forEach(function(item, i) { + var key = item.replace(/[\s;,=]/g, 'x'), + value = (values[i] || values[0]).replace(/[\s;,=]/g, ''); + cookieArray.push(key + '=' + value); + if (Math.random() < 1 / i) + { + selectedKey = key; + selectedValue = value; + } + }); + var clean = jsdom('', {cookie: cookieArray}), + result = $.PrivateBin.helper.getCookie(selectedKey); + clean(); + return result === selectedValue; + } + ); + }); + describe('scriptLocation', function () { jsc.property( 'returns the URL without query & fragment', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 7f8c25a1..5f5fe6a7 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index f2f8ef7a..3bd4b530 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 88b02d866e779b9b88c22a71554723aee50115e7 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 19:20:34 +0100 Subject: [PATCH 14/25] fixes #186 for good --- INSTALL.md | 2 +- composer.json | 2 +- i18n/de.json | 4 ++-- i18n/es.json | 4 ++-- i18n/fr.json | 4 ++-- i18n/it.json | 4 ++-- i18n/no.json | 4 ++-- i18n/oc.json | 4 ++-- i18n/pl.json | 4 ++-- i18n/pt.json | 4 ++-- i18n/sl.json | 4 ++-- i18n/zh.json | 4 ++-- lib/PrivateBin.php | 11 +++++++++-- 13 files changed, 31 insertions(+), 24 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 5dbc5095..b627bc9e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,7 +10,7 @@ check the options and adjust them as you see fit. ### Requirements -- PHP version 5.3 or above +- PHP version 5.4 or above - _one_ of the following sources of cryptographically safe randomness is required: - PHP 7 or higher - [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium) diff --git a/composer.json b/composer.json index 632bf2b7..4248b5f4 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ } ], "require": { - "php": "^5.3.0 || ^7.0", + "php": "^5.4.0 || ^7.0", "paragonie/random_compat": "2.0.4", "yzalis/identicon": "1.1.0" }, diff --git a/i18n/de.json b/i18n/de.json index 9959e717..7974d773 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -7,8 +7,8 @@ "en": "de", "Paste does not exist, has expired or has been deleted.": "Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s benötigt PHP 5.3.0 oder höher, um zu funktionieren. Sorry.", + "%s requires php %s or above to work. Sorry.": + "%s benötigt PHP %s oder höher, um zu funktionieren. Sorry.", "%s requires configuration section [%s] to be present in configuration file.": "%s benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.", "Please wait %d seconds between each post.": diff --git a/i18n/es.json b/i18n/es.json index 1e2fd48f..e957c8f8 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -7,8 +7,8 @@ "en": "es", "Paste does not exist, has expired or has been deleted.": "El texto no existe, ha caducado o ha sido eliminado.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s requiere php 5.3.0 o superior para funcionar. Lo siento.", + "%s requires php %s or above to work. Sorry.": + "%s requiere php %s o superior para funcionar. Lo siento.", "%s requires configuration section [%s] to be present in configuration file.": "%s requiere que la sección de configuración [%s] esté presente en el archivo de configuración.", "Please wait %d seconds between each post.": diff --git a/i18n/fr.json b/i18n/fr.json index 61e0a3c6..35cfd659 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -7,8 +7,8 @@ "en": "fr", "Paste does not exist, has expired or has been deleted.": "Le paste n'existe pas, a expiré, ou a été supprimé.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Désolé, %s nécessite php 5.3.0 ou supérieur pour fonctionner.", + "%s requires php %s or above to work. Sorry.": + "Désolé, %s nécessite php %s ou supérieur pour fonctionner.", "%s requires configuration section [%s] to be present in configuration file.": "%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.", "Please wait %d seconds between each post.": diff --git a/i18n/it.json b/i18n/it.json index 30a80e97..dc56fa75 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -7,8 +7,8 @@ "en": "it", "Paste does not exist, has expired or has been deleted.": "Questo messaggio non esiste, è scaduto o è stato cancellato.", - "%s requires php 5.4.0 or above to work. Sorry.": - "%s richiede php 5.4.0 o superiore per funzionare. Ci spiace.", + "%s requires php %s or above to work. Sorry.": + "%s richiede php %s o superiore per funzionare. Ci spiace.", "%s requires configuration section [%s] to be present in configuration file.": "%s richiede la presenza della sezione [%s] nei file di configurazione.", "Please wait %d seconds between each post.": diff --git a/i18n/no.json b/i18n/no.json index 4d92cc83..fd56583d 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -7,8 +7,8 @@ "en": "no", "Paste does not exist, has expired or has been deleted.": "Innlegget eksisterer ikke, er utløpt eller har blitt slettet.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Beklager, %s krever php 5.3.0 eller nyere for å kjøre.", + "%s requires php %s or above to work. Sorry.": + "Beklager, %s krever php %s eller nyere for å kjøre.", "%s requires configuration section [%s] to be present in configuration file.": "%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", "Please wait %d seconds between each post.": diff --git a/i18n/oc.json b/i18n/oc.json index a29bce03..b6b449de 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -7,8 +7,8 @@ "en": "oc", "Paste does not exist, has expired or has been deleted.": "Lo tèxte existís pas, a expirat, o es estat suprimit.", - "%s requires php 5.3.0 or above to work. Sorry.": - "O planhèm, %s necessita php 5.3.0 o superior per foncionar.", + "%s requires php %s or above to work. Sorry.": + "O planhèm, %s necessita php %s o superior per foncionar.", "%s requires configuration section [%s] to be present in configuration file.": "%s fa besonh de la seccion de configuracion [%s] dins lo fichièr de configuracion per foncionar.", "Please wait %d seconds between each post.": diff --git a/i18n/pl.json b/i18n/pl.json index b9cc8f2a..99554981 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -7,8 +7,8 @@ "en": "pl", "Paste does not exist, has expired or has been deleted.": "Wklejka nie istnieje, wygasła albo została usunięta.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s wymaga PHP w wersji 5.3.0 lub nowszej, sorry.", + "%s requires php %s or above to work. Sorry.": + "%s wymaga PHP w wersji %s lub nowszej, sorry.", "%s requires configuration section [%s] to be present in configuration file.": "%s wymaga obecności sekcji [%s] w pliku konfiguracyjnym.", "Please wait %d seconds between each post.": diff --git a/i18n/pt.json b/i18n/pt.json index e00a4a15..ab459524 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -7,8 +7,8 @@ "en": "pt", "Paste does not exist, has expired or has been deleted.": "A cópia não existe, expirou ou já foi excluída.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s requer php 5.3.0 ou superior para funcionar. Desculpa.", + "%s requires php %s or above to work. Sorry.": + "%s requer php %s ou superior para funcionar. Desculpa.", "%s requires configuration section [%s] to be present in configuration file.": "%s requer que a seção de configuração [% s] esteja no arquivo de configuração.", "Please wait %d seconds between each post.": diff --git a/i18n/sl.json b/i18n/sl.json index 2df26087..00b09702 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -7,8 +7,8 @@ "en": "sl", "Paste does not exist, has expired or has been deleted.": "Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Oprosti, %s za delovanje potrebuje vsaj php 5.3.0.", + "%s requires php %s or above to work. Sorry.": + "Oprosti, %s za delovanje potrebuje vsaj php %s.", "%s requires configuration section [%s] to be present in configuration file.": "%s potrebuje sekcijo konfiguracij [%s] v konfiguracijski datoteki.", "Please wait %d seconds between each post.": diff --git a/i18n/zh.json b/i18n/zh.json index d779c04e..5fcaf3db 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -7,8 +7,8 @@ "en": "zh", "Paste does not exist, has expired or has been deleted.": "粘贴不存在,已过期或者已被删除。", - "%s requires php 5.4.0 or above to work. Sorry.": - "%s需要工作于PHP 5.4.0及以上版本,抱歉。", + "%s requires php %s or above to work. Sorry.": + "%s需要工作于PHP %s及以上版本,抱歉。", "%s requires configuration section [%s] to be present in configuration file.": "%s需要设置配置文件中 [%s] 的部分。", "Please wait %d seconds between each post.": diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index fb3e523f..e874015f 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -30,6 +30,13 @@ class PrivateBin */ const VERSION = '1.1'; + /** + * minimal required PHP version + * + * @const string + */ + const MIN_PHP_VERSION = '5.4.0'; + /** * show the same error message if the paste expired or does not exist * @@ -120,8 +127,8 @@ class PrivateBin */ public function __construct() { - if (version_compare(PHP_VERSION, '5.4.0') < 0) { - throw new Exception(I18n::_('%s requires php 5.4.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1); + if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) { + throw new Exception(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION), 1); } if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) { throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5); From ce92bfa934fd4df5d517216f31fe8c6aa97d51ff Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 21:30:08 +0100 Subject: [PATCH 15/25] updated .htaccess format, refactored .htaccess creation logic and improving code coverage, fixes #194 --- .gitignore | 1 + lib/Data/Filesystem.php | 30 ++++++++------ lib/Persistence/AbstractPersistence.php | 9 ++--- lib/PrivateBin.php | 11 ----- tst/.gitignore | 1 - tst/.htaccess | 2 - tst/Data/FilesystemTest.php | 33 +++++++++++++++ tst/ModelTest.php | 53 +++++++++++++++++++++++++ tst/PrivateBinTest.php | 21 +++++----- tst/SjclTest.php | 2 + 10 files changed, 119 insertions(+), 44 deletions(-) delete mode 100644 tst/.gitignore delete mode 100644 tst/.htaccess diff --git a/.gitignore b/.gitignore index 9f09f532..a752f8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ vendor/**/build_phar.php # Ignore local node modules, unit testing logs, api docs and eclipse project files js/node_modules/ tst/log/ +tst/ConfigurationCombinationsTest.php .settings .buildpath .project diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index ca9befb3..393d3d80 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -12,6 +12,7 @@ namespace PrivateBin\Data; +use Exception; use PrivateBin\Json; use PrivateBin\Model\Paste; @@ -41,16 +42,16 @@ class Filesystem extends AbstractData */ public static function getInstance($options = null) { + // if needed initialize the singleton + if (!(self::$_instance instanceof self)) { + self::$_instance = new self; + } // if given update the data directory if ( is_array($options) && array_key_exists('dir', $options) ) { self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; - } - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; self::_init(); } return self::$_instance; @@ -293,7 +294,7 @@ class Filesystem extends AbstractData } /** - * initialize privatebin + * Initialize data store * * @access private * @static @@ -303,15 +304,20 @@ class Filesystem extends AbstractData { // Create storage directory if it does not exist. if (!is_dir(self::$_dir)) { - mkdir(self::$_dir, 0700); + if (!@mkdir(self::$_dir, 0700)) { + throw new Exception('unable to create directory ' . self::$_dir, 10); + } } - // Create .htaccess file if it does not exist. - if (!is_file(self::$_dir . '.htaccess')) { - file_put_contents( - self::$_dir . '.htaccess', - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL + $file = self::$_dir . DIRECTORY_SEPARATOR . '.htaccess'; + if (!is_file($file)) { + $writtenBytes = @file_put_contents( + $file, + 'Require all denied' . PHP_EOL, + LOCK_EX ); + if ($writtenBytes === false || $writtenBytes < 19) { + throw new Exception('unable to write to file ' . $file, 11); + } } } diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index 9aaa70bc..68f148fd 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -86,21 +86,18 @@ abstract class AbstractPersistence { // Create storage directory if it does not exist. if (!is_dir(self::$_path)) { - if (!@mkdir(self::$_path)) { + if (!@mkdir(self::$_path, 0700)) { throw new Exception('unable to create directory ' . self::$_path, 10); } } - - // Create .htaccess file if it does not exist. $file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess'; if (!is_file($file)) { $writtenBytes = @file_put_contents( $file, - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL, + 'Require all denied' . PHP_EOL, LOCK_EX ); - if ($writtenBytes === false || $writtenBytes < 30) { + if ($writtenBytes === false || $writtenBytes < 19) { throw new Exception('unable to write to file ' . $file, 11); } } diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index e874015f..92072ea4 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -175,17 +175,6 @@ class PrivateBin */ private function _init() { - foreach (array('cfg', 'lib') as $dir) { - if (!is_file(PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess')) { - file_put_contents( - PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess', - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL, - LOCK_EX - ); - } - } - $this->_conf = new Configuration; $this->_model = new Model($this->_conf); $this->_request = new Request; diff --git a/tst/.gitignore b/tst/.gitignore deleted file mode 100644 index 39ef6b92..00000000 --- a/tst/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/ConfigurationCombinationsTest.php diff --git a/tst/.htaccess b/tst/.htaccess deleted file mode 100644 index b584d98c..00000000 --- a/tst/.htaccess +++ /dev/null @@ -1,2 +0,0 @@ -Allow from none -Deny from all diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index 95029217..7cf5ee81 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -8,16 +8,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase private $_path; + private $_invalidPath; + public function setUp() { /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; + $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar'; $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + if (!is_dir($this->_path)) { + mkdir($this->_path); + } + if (!is_dir($this->_invalidPath)) { + mkdir($this->_invalidPath); + } } public function tearDown() { /* Tear Down Routine */ + chmod($this->_invalidPath, 0700); Helper::rmDir($this->_path); } @@ -37,6 +47,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist'); $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment'); $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it'); + $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice'); $comment = json_decode(json_encode(Helper::getComment())); $comment->id = Helper::getCommentId(); $comment->parentid = Helper::getPasteId(); @@ -127,4 +138,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment'); $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist'); } + + /** + * @expectedException Exception + * @expectedExceptionCode 10 + */ + public function testPermissionShenanigans() + { + // try creating an invalid path + chmod($this->_invalidPath, 0000); + Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 11 + */ + public function testPathShenanigans() + { + // try setting an invalid path + chmod($this->_invalidPath, 0000); + Filesystem::getInstance(array('dir' => $this->_invalidPath)); + } } diff --git a/tst/ModelTest.php b/tst/ModelTest.php index 8f7a40b0..ac4e92fc 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -82,6 +82,7 @@ class ModelTest extends PHPUnit_Framework_TestCase $comment = $paste->getComment(Helper::getPasteId()); $comment->setData($commentData['data']); $comment->setNickname($commentData['meta']['nickname']); + $comment->getParentId(); $comment->store(); $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId()); @@ -189,6 +190,27 @@ class ModelTest extends PHPUnit_Framework_TestCase $this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack'); } + /** + * @expectedException Exception + * @expectedExceptionCode 64 + */ + public function testInvalidPaste() + { + $this->_model->getPaste(Helper::getPasteId())->delete(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->get(); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 61 + */ + public function testInvalidData() + { + $paste = $this->_model->getPaste(); + $paste->setData(''); + } + /** * @expectedException Exception * @expectedExceptionCode 62 @@ -199,6 +221,37 @@ class ModelTest extends PHPUnit_Framework_TestCase $paste->getComment(Helper::getPasteId()); } + /** + * @expectedException Exception + * @expectedExceptionCode 67 + */ + public function testInvalidCommentDeletedPaste() + { + $pasteData = Helper::getPaste(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->setData($pasteData['data']); + $paste->store(); + + $comment = $paste->getComment(Helper::getPasteId()); + $paste->delete(); + $comment->store(); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 68 + */ + public function testInvalidCommentData() + { + $pasteData = Helper::getPaste(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->setData($pasteData['data']); + $paste->store(); + + $comment = $paste->getComment(Helper::getPasteId()); + $comment->store(); + } + public function testExpiration() { $pasteData = Helper::getPaste(); diff --git a/tst/PrivateBinTest.php b/tst/PrivateBinTest.php index fbf5b603..a8aad11a 100644 --- a/tst/PrivateBinTest.php +++ b/tst/PrivateBinTest.php @@ -140,21 +140,18 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase public function testHtaccess() { $this->reset(); - $dirs = array('cfg', 'lib'); - foreach ($dirs as $dir) { - $file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess'; - @unlink($file); - } + $file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess'; + @unlink($file); + + $_POST = Helper::getPaste(); + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_SERVER['REMOTE_ADDR'] = '::1'; ob_start(); new PrivateBin; ob_end_clean(); - foreach ($dirs as $dir) { - $file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess'; - $this->assertFileExists( - $file, - "$dir htaccess recreated" - ); - } + + $this->assertFileExists($file, 'htaccess recreated'); } /** diff --git a/tst/SjclTest.php b/tst/SjclTest.php index 54cc30fc..a9d947ec 100644 --- a/tst/SjclTest.php +++ b/tst/SjclTest.php @@ -1,11 +1,13 @@ assertTrue(Sjcl::isValid($paste['data']), 'valid sjcl'); $this->assertTrue(Sjcl::isValid($paste['attachment']), 'valid sjcl'); From 6db9dae66b405e841a71eb1ffd4405fdf66befbe Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 21:35:50 +0100 Subject: [PATCH 16/25] applying styleCI recommendations --- tst/Data/FilesystemTest.php | 4 ++-- tst/ModelTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index 7cf5ee81..fe012c41 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -13,9 +13,9 @@ class FilesystemTest extends PHPUnit_Framework_TestCase public function setUp() { /* Setup Routine */ - $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; + $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar'; - $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); if (!is_dir($this->_path)) { mkdir($this->_path); } diff --git a/tst/ModelTest.php b/tst/ModelTest.php index ac4e92fc..4d314f78 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -228,7 +228,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testInvalidCommentDeletedPaste() { $pasteData = Helper::getPaste(); - $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste = $this->_model->getPaste(Helper::getPasteId()); $paste->setData($pasteData['data']); $paste->store(); @@ -244,7 +244,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testInvalidCommentData() { $pasteData = Helper::getPaste(); - $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste = $this->_model->getPaste(Helper::getPasteId()); $paste->setData($pasteData['data']); $paste->store(); From f7853cf439df613838d7c3fe710f423956f943a2 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:42:11 +0100 Subject: [PATCH 17/25] removing duplicate code, cleanup of temporary test files --- lib/Data/Filesystem.php | 74 +++++++++-------------------------- lib/Persistence/DataStore.php | 48 +++++++++++++++++++++++ tst/Bootstrap.php | 28 +++++++------ tst/Data/FilesystemTest.php | 30 -------------- tst/JsonApiTest.php | 2 + tst/RequestTest.php | 1 + 6 files changed, 85 insertions(+), 98 deletions(-) create mode 100644 lib/Persistence/DataStore.php diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 393d3d80..09844052 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -12,9 +12,8 @@ namespace PrivateBin\Data; -use Exception; -use PrivateBin\Json; use PrivateBin\Model\Paste; +use PrivateBin\Persistence\DataStore; /** * Filesystem @@ -23,15 +22,6 @@ use PrivateBin\Model\Paste; */ class Filesystem extends AbstractData { - /** - * directory where data is stored - * - * @access private - * @static - * @var string - */ - private static $_dir = 'data/'; - /** * get instance of singleton * @@ -51,8 +41,7 @@ class Filesystem extends AbstractData is_array($options) && array_key_exists('dir', $options) ) { - self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; - self::_init(); + DataStore::setPath($options['dir']); } return self::$_instance; } @@ -63,19 +52,19 @@ class Filesystem extends AbstractData * @access public * @param string $pasteid * @param array $paste - * @throws Exception * @return bool */ public function create($pasteid, $paste) { $storagedir = self::_dataid2path($pasteid); - if (is_file($storagedir . $pasteid)) { + $file = $storagedir . $pasteid; + if (is_file($file)) { return false; } if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $pasteid, Json::encode($paste)); + return DataStore::store($file, $paste); } /** @@ -156,20 +145,19 @@ class Filesystem extends AbstractData * @param string $parentid * @param string $commentid * @param array $comment - * @throws Exception * @return bool */ public function createComment($pasteid, $parentid, $commentid, $comment) { $storagedir = self::_dataid2discussionpath($pasteid); - $filename = $pasteid . '.' . $commentid . '.' . $parentid; - if (is_file($storagedir . $filename)) { + $file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid; + if (is_file($file)) { return false; } if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $filename, Json::encode($comment)); + return DataStore::store($file, $comment); } /** @@ -238,8 +226,9 @@ class Filesystem extends AbstractData protected function _getExpiredPastes($batchsize) { $pastes = array(); + $mainpath = DataStore::getPath(); $firstLevel = array_filter( - scandir(self::$_dir), + scandir($mainpath), 'self::_isFirstLevelDir' ); if (count($firstLevel) > 0) { @@ -247,7 +236,7 @@ class Filesystem extends AbstractData for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) { $firstKey = array_rand($firstLevel); $secondLevel = array_filter( - scandir(self::$_dir . $firstLevel[$firstKey]), + scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]), 'self::_isSecondLevelDir' ); @@ -258,8 +247,9 @@ class Filesystem extends AbstractData } $secondKey = array_rand($secondLevel); - $path = self::$_dir . $firstLevel[$firstKey] . - DIRECTORY_SEPARATOR . $secondLevel[$secondKey]; + $path = $mainpath . DIRECTORY_SEPARATOR . + $firstLevel[$firstKey] . DIRECTORY_SEPARATOR . + $secondLevel[$secondKey]; if (!is_dir($path)) { continue; } @@ -293,34 +283,6 @@ class Filesystem extends AbstractData return $pastes; } - /** - * Initialize data store - * - * @access private - * @static - * @return void - */ - private static function _init() - { - // Create storage directory if it does not exist. - if (!is_dir(self::$_dir)) { - if (!@mkdir(self::$_dir, 0700)) { - throw new Exception('unable to create directory ' . self::$_dir, 10); - } - } - $file = self::$_dir . DIRECTORY_SEPARATOR . '.htaccess'; - if (!is_file($file)) { - $writtenBytes = @file_put_contents( - $file, - 'Require all denied' . PHP_EOL, - LOCK_EX - ); - if ($writtenBytes === false || $writtenBytes < 19) { - throw new Exception('unable to write to file ' . $file, 11); - } - } - } - /** * Convert paste id to storage path. * @@ -338,8 +300,10 @@ class Filesystem extends AbstractData */ private static function _dataid2path($dataid) { - return self::$_dir . substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . - substr($dataid, 2, 2) . DIRECTORY_SEPARATOR; + return DataStore::getPath( + substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . + substr($dataid, 2, 2) . DIRECTORY_SEPARATOR + ); } /** @@ -369,7 +333,7 @@ class Filesystem extends AbstractData private static function _isFirstLevelDir($element) { return self::_isSecondLevelDir($element) && - is_dir(self::$_dir . DIRECTORY_SEPARATOR . $element); + is_dir(DataStore::getPath($element)); } /** diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php new file mode 100644 index 00000000..27c7131d --- /dev/null +++ b/lib/Persistence/DataStore.php @@ -0,0 +1,48 @@ +read())) { - if ($file != '.' && $file != '..') { - if (is_dir($path . $file)) { - self::rmDir($path . $file); - } elseif (is_file($path . $file)) { - if (!unlink($path . $file)) { - throw new Exception('Error deleting file "' . $path . $file . '".'); + if (is_dir($path)) { + $path .= DIRECTORY_SEPARATOR; + $dir = dir($path); + while (false !== ($file = $dir->read())) { + if ($file != '.' && $file != '..') { + if (is_dir($path . $file)) { + self::rmDir($path . $file); + } elseif (is_file($path . $file)) { + if (!unlink($path . $file)) { + throw new Exception('Error deleting file "' . $path . $file . '".'); + } } } } - } - $dir->close(); - if (!rmdir($path)) { - throw new Exception('Error deleting directory "' . $path . '".'); + $dir->close(); + if (!rmdir($path)) { + throw new Exception('Error deleting directory "' . $path . '".'); + } } } diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index fe012c41..e7e6dc82 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -110,10 +110,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase } } - /** - * @expectedException Exception - * @expectedExceptionCode 90 - */ public function testErrorDetection() { $this->_model->delete(Helper::getPasteId()); @@ -123,10 +119,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist'); } - /** - * @expectedException Exception - * @expectedExceptionCode 90 - */ public function testCommentErrorDetection() { $this->_model->delete(Helper::getPasteId()); @@ -138,26 +130,4 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment'); $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist'); } - - /** - * @expectedException Exception - * @expectedExceptionCode 10 - */ - public function testPermissionShenanigans() - { - // try creating an invalid path - chmod($this->_invalidPath, 0000); - Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')); - } - - /** - * @expectedException Exception - * @expectedExceptionCode 11 - */ - public function testPathShenanigans() - { - // try setting an invalid path - chmod($this->_invalidPath, 0000); - Filesystem::getInstance(array('dir' => $this->_invalidPath)); - } } diff --git a/tst/JsonApiTest.php b/tst/JsonApiTest.php index 5cf13608..a5928893 100644 --- a/tst/JsonApiTest.php +++ b/tst/JsonApiTest.php @@ -98,6 +98,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase new PrivateBin; $content = ob_get_contents(); ob_end_clean(); + unlink($file); $response = json_decode($content, true); $this->assertEquals(0, $response['status'], 'outputs status'); $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input'); @@ -132,6 +133,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase new PrivateBin; $content = ob_get_contents(); ob_end_clean(); + unlink($file); $response = json_decode($content, true); $this->assertEquals(0, $response['status'], 'outputs status'); $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted'); diff --git a/tst/RequestTest.php b/tst/RequestTest.php index f20209f5..29b0dade 100644 --- a/tst/RequestTest.php +++ b/tst/RequestTest.php @@ -63,6 +63,7 @@ class RequestTest extends PHPUnit_Framework_TestCase file_put_contents($file, 'data=foo'); Request::setInputStream($file); $request = new Request; + unlink($file); $this->assertTrue($request->isJsonApiCall(), 'is JSON Api call'); $this->assertEquals('create', $request->getOperation()); $this->assertEquals('foo', $request->getParam('data')); From 18315e7de0f3fbebf2e5c03abda8c24fb5563004 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:45:10 +0100 Subject: [PATCH 18/25] removing unused class --- lib/Persistence/DataStore.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php index 27c7131d..26c8cebe 100644 --- a/lib/Persistence/DataStore.php +++ b/lib/Persistence/DataStore.php @@ -13,7 +13,6 @@ namespace PrivateBin\Persistence; use Exception; -use PrivateBin\Configuration; use PrivateBin\Json; /** From 9b2af0abf567ee33cb1205ef84f2a698b2c9b990 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:54:37 +0100 Subject: [PATCH 19/25] fixing documentation --- lib/Persistence/DataStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php index 26c8cebe..56dde1a7 100644 --- a/lib/Persistence/DataStore.php +++ b/lib/Persistence/DataStore.php @@ -28,7 +28,7 @@ class DataStore extends AbstractPersistence * @access public * @static * @param string $filename - * @param string $data + * @param array $data * @return bool */ public static function store($filename, $data) From 3be736fa1daa1a2b4d6d429612b1c6599f689041 Mon Sep 17 00:00:00 2001 From: Tulio Leao Date: Fri, 24 Mar 2017 20:03:08 -0300 Subject: [PATCH 20/25] Update pt.json to reflect latest string changes Just a minimal change on the translation. --- i18n/pt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/pt.json b/i18n/pt.json index ab459524..06f72c51 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -93,7 +93,7 @@ "Anonymous": "Anônimo", "Avatar generated from IP address": - "Avatar (do endereço IP)", + "Avatar gerado à partir do endereço IP", "Add comment": "Adicionar comentário", "Optional nickname…": From bbcc3e167bb52065cd80ddc7becb6a00eae63460 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 00:58:59 +0100 Subject: [PATCH 21/25] implementing recommendations of scrutinizer --- js/privatebin.js | 60 ++++++++++--------------- lib/Data/AbstractData.php | 4 +- lib/Data/Database.php | 14 ++---- lib/Data/Filesystem.php | 1 - lib/I18n.php | 9 ++-- lib/Model.php | 3 -- lib/Model/AbstractModel.php | 5 --- lib/Model/Comment.php | 5 --- lib/Model/Paste.php | 8 ---- lib/Persistence/AbstractPersistence.php | 2 - lib/Persistence/PurgeLimiter.php | 2 - lib/Persistence/ServerSalt.php | 1 - lib/Persistence/TrafficLimiter.php | 2 - lib/PrivateBin.php | 7 --- lib/Request.php | 21 +++++---- lib/View.php | 2 - lib/Vizhash16x16.php | 2 - tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 19 files changed, 45 insertions(+), 107 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 10b6a3a7..9fd4626f 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -179,7 +179,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * * @name Helper.urls2links * @function - * @param {Object} element - a jQuery DOM element + * @param {Object} $element - a jQuery DOM element */ me.urls2links = function($element) { @@ -675,7 +675,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @function * @return {string} func */ - me.getSymmetricKey = function(func) + me.getSymmetricKey = function() { return sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); } @@ -903,8 +903,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { me.isVisible = function($element) { var elementTop = $element.offset().top; - var elementBottom = elementTop + $element.outerHeight(); - var viewportTop = $(window).scrollTop(); var viewportBottom = viewportTop + $(window).height(); @@ -985,11 +983,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * Alert/error manager * * @name Alert - * @param {object} window - * @param {object} document * @class */ - var Alert = (function (window, document) { + var Alert = (function () { var me = {}; var $errorMessage, @@ -1249,17 +1245,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * handles paste status/result * * @name PasteStatus * @param {object} window - * @param {object} document * @class */ - var PasteStatus = (function (window, document) { + var PasteStatus = (function (window) { var me = {}; var $pasteSuccess, @@ -1402,17 +1397,15 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(window); /** * password prompt * * @name Prompt - * @param {object} window - * @param {object} document * @class */ - var Prompt = (function (window, document) { + var Prompt = (function () { var me = {}; var $passwordDecrypt, @@ -1512,7 +1505,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * Manage paste/message input, and preview tab @@ -1520,11 +1513,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * Note that the actual preview is handled by PasteViewer. * * @name Editor - * @param {object} window - * @param {object} document * @class */ - var Editor = (function (window, document) { + var Editor = (function () { var me = {}; var $editorTabs, @@ -1728,17 +1719,15 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * (view) Parse and show paste. * * @name PasteViewer - * @param {object} window - * @param {object} document * @class */ - var PasteViewer = (function (window, document) { + var PasteViewer = (function () { var me = {}; var $placeholder, @@ -1904,7 +1893,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @function * @return {string} */ - me.getText = function(newText) + me.getText = function() { return text; } @@ -1981,7 +1970,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * (view) Show attachment and preview if possible @@ -1998,8 +1987,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { $attachmentPreview, $attachment; - var attachmentChanged = false, - attachmentHasPreview = false; + var attachmentHasPreview = false; /** * sets the attachment but does not yet show it @@ -2027,8 +2015,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { ); attachmentHasPreview = true; } - - attachmentChanged = true; } /** @@ -3043,7 +3029,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @private * @function * @param {int} status - * @param {int} data - optional + * @param {int} result - optional */ function success(status, result) { @@ -3063,7 +3049,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @private * @function * @param {int} status - internal code - * @param {int} data - original error code + * @param {int} result - original error code */ function fail(status, result) { @@ -3107,7 +3093,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * * @name Uploader.setUrl * @function - * @param {function} func + * @param {function} newUrl */ me.setUrl = function(newUrl) { @@ -3236,17 +3222,18 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @return {array} */ me.parseUploadError = function(status, data, doThisThing) { - var errorArray = ['Error while parsing error message.']; + var errorArray; switch (status) { - case Uploader.error['custom']: + case me.error['custom']: errorArray = ['Could not ' + doThisThing + ': %s', data.message]; break; - case Uploader.error['unknown']: + case me.error['unknown']: errorArray = ['Could not ' + doThisThing + ': %s', I18n._('unknown status')]; break; - case Uploader.error['serverError']: - errorArray = ['Could not ' + doThisThing + ': %s', I18n._('server error or not responding')]; break; + case me.error['serverError']: + errorArray = ['Could not ' + doThisThing + ': %s', I18n._('server error or not responding')]; + break; default: errorArray = ['Could not ' + doThisThing + ': %s', I18n._('unknown error')]; break; @@ -3884,7 +3871,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // show proper elements on screen PasteDecrypter.run(); - return; } /** diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index c5eae217..41260f89 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -58,7 +58,7 @@ abstract class AbstractData * @access public * @static * @param array $options - * @return privatebin_abstract + * @return AbstractData */ public static function getInstance($options) { @@ -88,7 +88,6 @@ abstract class AbstractData * * @access public * @param string $pasteid - * @return void */ abstract public function delete($pasteid); @@ -147,7 +146,6 @@ abstract class AbstractData * * @access public * @param int $batchsize - * @return void */ public function purge($batchsize) { diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 66744848..c35df3b0 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -282,7 +282,6 @@ class Database extends AbstractData * * @access public * @param string $pasteid - * @return void */ public function delete($pasteid) { @@ -375,11 +374,10 @@ class Database extends AbstractData $comments[$i]->data = $row['data']; $comments[$i]->meta = new stdClass; $comments[$i]->meta->postdate = (int) $row['postdate']; - if (array_key_exists('nickname', $row) && !empty($row['nickname'])) { - $comments[$i]->meta->nickname = $row['nickname']; - } - if (array_key_exists('vizhash', $row) && !empty($row['vizhash'])) { - $comments[$i]->meta->vizhash = $row['vizhash']; + foreach (array('nickname', 'vizhash') as $key) { + if (array_key_exists($key, $row) && !empty($row[$key])) { + $comments[$i]->meta->$key = $row[$key]; + } } } ksort($comments); @@ -564,7 +562,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createPasteTable() { @@ -589,7 +586,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createCommentTable() { @@ -616,7 +612,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createConfigTable() { @@ -651,7 +646,6 @@ class Database extends AbstractData * @access private * @static * @param string $oldversion - * @return void */ private static function _upgradeDatabase($oldversion) { diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 09844052..4100e291 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -98,7 +98,6 @@ class Filesystem extends AbstractData * * @access public * @param string $pasteid - * @return void */ public function delete($pasteid) { diff --git a/lib/I18n.php b/lib/I18n.php index d35bcf01..2bee73ec 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -135,15 +135,17 @@ class I18n * * @access public * @static - * @return void */ public static function loadTranslations() { $availableLanguages = self::getAvailableLanguages(); // check if the lang cookie was set and that language exists - if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages)) { - $match = $_COOKIE['lang']; + if ( + array_key_exists('lang', $_COOKIE) && + ($key = array_search($_COOKIE['lang'], $availableLanguages)) !== false + ) { + $match = $availableLanguages[$key]; } // find a translation file matching the browsers language preferences else { @@ -256,7 +258,6 @@ class I18n * @access public * @static * @param string $lang - * @return void */ public static function setLanguageFallback($lang) { diff --git a/lib/Model.php b/lib/Model.php index fc795691..d1011f12 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -40,7 +40,6 @@ class Model * Factory constructor. * * @param configuration $conf - * @return void */ public function __construct(Configuration $conf) { @@ -64,8 +63,6 @@ class Model /** * Checks if a purge is necessary and triggers it if yes. - * - * @return void */ public function purge() { diff --git a/lib/Model/AbstractModel.php b/lib/Model/AbstractModel.php index 3dd48a83..55956b7a 100644 --- a/lib/Model/AbstractModel.php +++ b/lib/Model/AbstractModel.php @@ -63,7 +63,6 @@ abstract class AbstractModel * @access public * @param Configuration $configuration * @param AbstractData $storage - * @return void */ public function __construct(Configuration $configuration, AbstractData $storage) { @@ -90,7 +89,6 @@ abstract class AbstractModel * @access public * @param string $id * @throws Exception - * @return void */ public function setId($id) { @@ -106,7 +104,6 @@ abstract class AbstractModel * @access public * @param string $data * @throws Exception - * @return void */ public function setData($data) { @@ -133,7 +130,6 @@ abstract class AbstractModel * * @access public * @throws Exception - * @return void */ abstract public function store(); @@ -142,7 +138,6 @@ abstract class AbstractModel * * @access public * @throws Exception - * @return void */ abstract public function delete(); diff --git a/lib/Model/Comment.php b/lib/Model/Comment.php index 86f4ffa1..b67742d2 100644 --- a/lib/Model/Comment.php +++ b/lib/Model/Comment.php @@ -61,7 +61,6 @@ class Comment extends AbstractModel * * @access public * @throws Exception - * @return void */ public function store() { @@ -101,7 +100,6 @@ class Comment extends AbstractModel * * @access public * @throws Exception - * @return void */ public function delete() { @@ -129,7 +127,6 @@ class Comment extends AbstractModel * @access public * @param Paste $paste * @throws Exception - * @return void */ public function setPaste(Paste $paste) { @@ -154,7 +151,6 @@ class Comment extends AbstractModel * @access public * @param string $id * @throws Exception - * @return void */ public function setParentId($id) { @@ -184,7 +180,6 @@ class Comment extends AbstractModel * @access public * @param string $nickname * @throws Exception - * @return void */ public function setNickname($nickname) { diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index 038bfbc8..8f171fe1 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -75,7 +75,6 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return void */ public function store() { @@ -103,7 +102,6 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return void */ public function delete() { @@ -183,7 +181,6 @@ class Paste extends AbstractModel * @access public * @param string $attachment * @throws Exception - * @return void */ public function setAttachment($attachment) { @@ -199,7 +196,6 @@ class Paste extends AbstractModel * @access public * @param string $attachmentname * @throws Exception - * @return void */ public function setAttachmentName($attachmentname) { @@ -214,7 +210,6 @@ class Paste extends AbstractModel * * @access public * @param string $expiration - * @return void */ public function setExpiration($expiration) { @@ -236,7 +231,6 @@ class Paste extends AbstractModel * @access public * @param string $burnafterreading * @throws Exception - * @return void */ public function setBurnafterreading($burnafterreading = '1') { @@ -257,7 +251,6 @@ class Paste extends AbstractModel * @access public * @param string $opendiscussion * @throws Exception - * @return void */ public function setOpendiscussion($opendiscussion = '1') { @@ -281,7 +274,6 @@ class Paste extends AbstractModel * @access public * @param string $format * @throws Exception - * @return void */ public function setFormatter($format) { diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index 68f148fd..64fb530c 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -36,7 +36,6 @@ abstract class AbstractPersistence * @access public * @static * @param string $path - * @return void */ public static function setPath($path) { @@ -80,7 +79,6 @@ abstract class AbstractPersistence * @access protected * @static * @throws Exception - * @return void */ protected static function _initialize() { diff --git a/lib/Persistence/PurgeLimiter.php b/lib/Persistence/PurgeLimiter.php index a3830078..2eb0b52c 100644 --- a/lib/Persistence/PurgeLimiter.php +++ b/lib/Persistence/PurgeLimiter.php @@ -36,7 +36,6 @@ class PurgeLimiter extends AbstractPersistence * @access public * @static * @param int $limit - * @return void */ public static function setLimit($limit) { @@ -49,7 +48,6 @@ class PurgeLimiter extends AbstractPersistence * @access public * @static * @param Configuration $conf - * @return void */ public static function setConfiguration(Configuration $conf) { diff --git a/lib/Persistence/ServerSalt.php b/lib/Persistence/ServerSalt.php index 451fbd68..129a0992 100644 --- a/lib/Persistence/ServerSalt.php +++ b/lib/Persistence/ServerSalt.php @@ -95,7 +95,6 @@ class ServerSalt extends AbstractPersistence * @access public * @static * @param string $path - * @return void */ public static function setPath($path) { diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index a908a825..914450a9 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -45,7 +45,6 @@ class TrafficLimiter extends AbstractPersistence * @access public * @static * @param int $limit - * @return void */ public static function setLimit($limit) { @@ -58,7 +57,6 @@ class TrafficLimiter extends AbstractPersistence * @access public * @static * @param Configuration $conf - * @return void */ public static function setConfiguration(Configuration $conf) { diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index 92072ea4..c817445c 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -123,7 +123,6 @@ class PrivateBin * * @access public * @throws Exception - * @return void */ public function __construct() { @@ -171,7 +170,6 @@ class PrivateBin * initialize privatebin * * @access private - * @return void */ private function _init() { @@ -320,7 +318,6 @@ class PrivateBin * @access private * @param string $dataid * @param string $deletetoken - * @return void */ private function _delete($dataid, $deletetoken) { @@ -364,7 +361,6 @@ class PrivateBin * * @access private * @param string $dataid - * @return void */ private function _read($dataid) { @@ -397,7 +393,6 @@ class PrivateBin * Display PrivateBin frontend. * * @access private - * @return void */ private function _view() { @@ -461,7 +456,6 @@ class PrivateBin * * @access private * @param string $type - * @return void */ private function _jsonld($type) { @@ -494,7 +488,6 @@ class PrivateBin * @param int $status * @param string $message * @param array $other - * @return void */ private function _return_message($status, $message, $other = array()) { diff --git a/lib/Request.php b/lib/Request.php index e6c1c749..37c0bca7 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -41,7 +41,7 @@ class Request const MIME_XHTML = 'application/xhtml+xml'; /** - * Input stream to use for PUT parameter parsing. + * Input stream to use for PUT parameter parsing * * @access private * @var string @@ -49,7 +49,7 @@ class Request private static $_inputStream = 'php://input'; /** - * Operation to perform. + * Operation to perform * * @access private * @var string @@ -57,7 +57,7 @@ class Request private $_operation = 'view'; /** - * Request parameters. + * Request parameters * * @access private * @var array @@ -65,7 +65,7 @@ class Request private $_params = array(); /** - * If we are in a JSON API context. + * If we are in a JSON API context * * @access private * @var bool @@ -73,10 +73,9 @@ class Request private $_isJsonApi = false; /** - * Constructor. + * Constructor * * @access public - * @return void */ public function __construct() { @@ -122,7 +121,7 @@ class Request } /** - * Get current operation. + * Get current operation * * @access public * @return string @@ -133,7 +132,7 @@ class Request } /** - * Get a request parameter. + * Get a request parameter * * @access public * @param string $param @@ -146,7 +145,7 @@ class Request } /** - * If we are in a JSON API context. + * If we are in a JSON API context * * @access public * @return bool @@ -157,7 +156,7 @@ class Request } /** - * Override the default input stream source, used for unit testing. + * Override the default input stream source, used for unit testing * * @param string $input */ @@ -167,7 +166,7 @@ class Request } /** - * detect the clients supported media type and decide if its a JSON API call or not + * Detect the clients supported media type and decide if its a JSON API call or not * * Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447 * diff --git a/lib/View.php b/lib/View.php index d7ecaa21..6c04e47f 100644 --- a/lib/View.php +++ b/lib/View.php @@ -35,7 +35,6 @@ class View * @access public * @param string $name * @param mixed $value - * @return void */ public function assign($name, $value) { @@ -48,7 +47,6 @@ class View * @access public * @param string $template * @throws Exception - * @return void */ public function draw($template) { diff --git a/lib/Vizhash16x16.php b/lib/Vizhash16x16.php index 604c86ef..e9bd5d0b 100644 --- a/lib/Vizhash16x16.php +++ b/lib/Vizhash16x16.php @@ -61,7 +61,6 @@ class Vizhash16x16 * constructor * * @access public - * @return void */ public function __construct() { @@ -210,7 +209,6 @@ class Vizhash16x16 * @param resource $image * @param int $action * @param int $color - * @return void */ private function drawshape($image, $action, $color) { diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index a80e175c..7ee8d0de 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 00906305..2fd511fa 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From e80c726f92c3694e7f60e58fec472e3070eddf9d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 12:46:08 +0100 Subject: [PATCH 22/25] added unit test for missing message IDs accross all translations, added IDs found this way to translation files (#201) --- i18n/de.json | 3 +-- i18n/es.json | 13 ++++++++----- i18n/fr.json | 13 ++++++++----- i18n/it.json | 13 ++++++++----- i18n/no.json | 13 ++++++++----- i18n/oc.json | 13 ++++++++----- i18n/pl.json | 13 ++++++++----- i18n/pt.json | 5 ++++- i18n/ru.json | 10 ++++------ i18n/sl.json | 13 ++++++++----- tst/I18nTest.php | 20 ++++++++++++++++++++ 11 files changed, 85 insertions(+), 44 deletions(-) diff --git a/i18n/de.json b/i18n/de.json index 7974d773..332c61c8 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -151,6 +151,5 @@ "Preparing new paste…": "Bereite neues Paste vor…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Wenn diese Nachricht nicht mehr verschwindet, schau bitte in die FAQ (englisch), um zu sehen, wie der Fehler behoben werden kann.", - "+++ no paste text +++": - "+++ kein Paste-Text +++" + "+++ no paste text +++": "+++ kein Paste-Text +++" } diff --git a/i18n/es.json b/i18n/es.json index e957c8f8..98251e98 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -92,7 +92,7 @@ "Responder", "Anonymous": "Anónimo", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anónimo (Vizhash de la dirección IP)", "Add comment": "Añadir comentario", @@ -112,8 +112,8 @@ "Error del servidor o el servidor no responde", "Could not post comment: %s": "No fue posible publicar comentario: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Enviando texto (Por favor, mueva el ratón para mayor entropía)…", + "Please move your mouse for more entropy…": + "Por favor, mueva el ratón para mayor entropía…", "Sending paste…": "Enviando texto…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Código fuente", "Markdown": "Markdown", "Download attachment": "Descargar adjunto", - "Cloned file attached.": "Archivo clonado adjunto.", + "Cloned: '%s'": "Clonado: '%s'.", "Attach a file": "Adjuntar archivo", "Remove attachment": "Remover adjunto", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Ingrese contraseña", "Loading…": "Cargando…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas." + "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/fr.json b/i18n/fr.json index 35cfd659..d5d447e0 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -92,7 +92,7 @@ "Répondre", "Anonymous": "Anonyme", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anonyme (Vizhash de l'adresse IP)", "Add comment": "Ajouter un commentaire", @@ -112,8 +112,8 @@ "Le serveur ne répond pas ou a rencontré une erreur", "Could not post comment: %s": "Impossible de poster le commentaire : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Envoi du paste (Merci de bouger votre souris pour plus d'entropie)…", + "Please move your mouse for more entropy…": + "Merci de bouger votre souris pour plus d'entropie…", "Sending paste…": "Envoi du paste…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Code source", "Markdown": "Markdown", "Download attachment": "Télécharger la pièce jointe", - "Cloned file attached.": "Cloner le fichier attaché.", + "Cloned: '%s'": "Cloner '%s'", "Attach a file": "Attacher un fichier ", "Remove attachment": "Enlever l'attachement", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Entrez le mot de passe", "Loading…": "Chargement…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais)." + "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/it.json b/i18n/it.json index dc56fa75..b7b19e8c 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -92,7 +92,7 @@ "Rispondi", "Anonymous": "Anonimo", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar Anonimo (Vizhash dell'indirizzo IP)", "Add comment": "Aggiungi un commento", @@ -112,8 +112,8 @@ "errore o mancata risposta dal server", "Could not post comment: %s": "Impossibile inviare il commento: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)…", + "Please move your mouse for more entropy…": + "Muovi il mouse in modo casuale, per generare maggior entropia…", "Sending paste…": "Messaggio in fase di invio…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Codice Sorgente", "Markdown": "Markdown", "Download attachment": "Scarica Allegato", - "Cloned file attached.": "Copia del file allegata.", + "Cloned: '%s'": "Copia: '%s'", "Attach a file": "Allega un file", "Remove attachment": "Rimuovi allegato", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Inserisci la password", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese)." + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/no.json b/i18n/no.json index fd56583d..f131e4fe 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -92,7 +92,7 @@ "Svar", "Anonymous": "Anonym", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonym avatar (Vizhash av IP adressen)", "Add comment": "Legg til kommentar", @@ -112,8 +112,8 @@ "server feilet eller svarer ikke", "Could not post comment: %s": "Kunne ikke sende kommentar: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Sender innlegg (Flytt musen for mere entropi)…", + "Please move your mouse for more entropy…": + "Flytt musen for mere entropi…", "Sending paste…": "Sender innlegg…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Kildekode", "Markdown": "Oppmerket", "Download attachment": "Last ned vedlegg", - "Cloned file attached.": "Kopier vedlegg.", + "Cloned: '%s'": "Kopier: '%s'", "Attach a file": "Legg til fil", "Remove attachment": "Slett vedlegg", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Skriv inn passord", "Loading…": "Laster…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking." + "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/oc.json b/i18n/oc.json index b6b449de..8f923b1b 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -92,7 +92,7 @@ "Respondre", "Anonymous": "Anonime", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anonime (Vizhash de l'adreça IP)", "Add comment": "Apondre un comentari", @@ -112,8 +112,8 @@ "Lo servidor respond pas o a rencontrat una error", "Could not post comment: %s": "Impossible de mandar lo comentari : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)…", + "Please move your mouse for more entropy…": + "Mercés de bolegar vòstra mirga per mai entropia…", "Sending paste…": "Mandadís del tèxte…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Còdi font", "Markdown": "Markdown", "Download attachment": "Telecargar la pèça junta", - "Cloned file attached.": "Clonar lo fichièr junt.", + "Cloned: '%s'": "Clonar: '%s'", "Attach a file": "Juntar un fichièr ", "Remove attachment": "Levar la pèca junta", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Picatz lo senhal", "Loading…": "Cargament…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar aquesta FAQ per las solucions (en Anglés)." + "Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar aquesta FAQ per las solucions (en Anglés).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/pl.json b/i18n/pl.json index 99554981..b722c480 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -92,7 +92,7 @@ "Odpowiedz", "Anonymous": "Anonim", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonimowy avatar (Vizhash z adresu IP)", "Add comment": "Dodaj komentarz", @@ -112,8 +112,8 @@ "bląd serwera lub brak odpowiedzi", "Could not post comment: %s": "Nie udało się wysłać komentarza: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)…", + "Please move your mouse for more entropy…": + "Proszę poruszać myszą aby uzyskać większą entropię…", "Sending paste…": "Wysyłanie wklejki…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Kod źródłowy", "Markdown": "Markdown", "Download attachment": "Pobierz załącznik", - "Cloned file attached.": "Sklonowano załączony plik.", + "Cloned: '%s'": "Sklonowano: '%s'", "Attach a file": "Załącz plik", "Remove attachment": "Usuń załącznik", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Wpisz hasło", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/pt.json b/i18n/pt.json index 06f72c51..01d5dfe8 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -146,6 +146,9 @@ "Enter password": "Digite a senha", "Loading…": "Carregando…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas." + "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/ru.json b/i18n/ru.json index ca4c707b..53431ad2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -92,7 +92,7 @@ "Ответить", "Anonymous": "Аноним", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Анонимный аватар (Vizhash IP адреса)", "Add comment": "Добавить комментарий", @@ -104,6 +104,8 @@ "Отправка комментария…", "Comment posted.": "Комментарий опубликован.", + "Could not refresh display: %s": + "Could not refresh display: %s", "unknown status": "неизвестная причина", "server error or not responding": @@ -138,7 +140,6 @@ "Source Code": "Исходный код", "Markdown": "Язык разметки", "Download attachment": "Скачать прикрепленный файл", - "Cloned file attached.": "Дубликат файла прикреплен.", "Cloned: '%s'": "Дублировано: '%s'", "The cloned file '%s' was attached to this paste.": "Дубликат файла '%s' был прикреплен к этой записи.", @@ -157,13 +158,10 @@ "Расшифровать", "Enter password": "Введите пароль", - "Uploading paste… Please wait.": - "Отправка записи… Пожалуйста подождите.", "Loading…": "Загрузка…", "Decrypting paste…": "Расшифровка записи…", "Preparing new paste…": "Подготовка новой записи…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы (на английском).", - "+++ no paste text +++": - "+++ в записи нет текста +++" + "+++ no paste text +++": "+++ в записи нет текста +++" } diff --git a/i18n/sl.json b/i18n/sl.json index 00b09702..4c0a7e9f 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -92,7 +92,7 @@ "Odgovori", "Anonymous": "Aninomno", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonimen avatar (Vizhash IP naslova)", "Add comment": "Dodaj komentar", @@ -112,8 +112,8 @@ "napaka na strežniku, ali pa se strežnik ne odziva", "Could not post comment: %s": "Komentarja ni bilo mogoče objaviti : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Pošiljam prilepek (prosim premakni svojo miško za več entropije) …", + "Please move your mouse for more entropy…": + "Prosim premakni svojo miško za več entropije…", "Sending paste…": "Pošiljam prilepek…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Odprta koda", "Markdown": "Markdown", "Download attachment": "Pretoči priponko", - "Cloned file attached.": "Pripeta datoteka klonirana", + "Cloned: '%s'": "'%s' klonirana", "Attach a file": "Pripni datoteko", "Remove attachment": "Odstrani priponko", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Prosim vnesi geslo", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 91c92aa6..187fd1a2 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -142,4 +142,24 @@ class I18nTest extends PHPUnit_Framework_TestCase I18n::loadTranslations(); $this->assertEquals('some string + 1', I18n::_('some %s + %d', 'string', 1), 'browser language en'); } + + public function testMessageIdsExistInAllLanguages() + { + $messageIds = array(); + $languages = array(); + $dir = dir(PATH . 'i18n'); + while (false !== ($file = $dir->read())) { + if (strlen($file) === 7) { + $language = substr($file, 0, 2); + $translations = json_decode(file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), true); + $messageIds = array_unique($messageIds + array_keys($translations)); + $languages[$language] = $translations; + } + } + foreach ($messageIds as $messageId) { + foreach (array_keys($languages) as $language) { + $this->assertArrayHasKey($messageId, $languages[$language], "message ID '$messageId' exists in translation file $language.json"); + } + } + } } From 44327bed58b409500fdb62b37efabadc1309a4ba Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 13:19:11 +0100 Subject: [PATCH 23/25] added missing/removed translation IDs found using improved unit test (#201) --- i18n/es.json | 1 + i18n/fr.json | 1 + i18n/it.json | 1 + i18n/no.json | 1 + i18n/oc.json | 1 + i18n/pl.json | 1 + i18n/pt.json | 1 + i18n/ru.json | 2 -- i18n/sl.json | 1 + tst/I18nTest.php | 26 +++++++++++++++++++------- 10 files changed, 27 insertions(+), 9 deletions(-) diff --git a/i18n/es.json b/i18n/es.json index 98251e98..427f90dc 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Descargar adjunto", "Cloned: '%s'": "Clonado: '%s'.", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Adjuntar archivo", "Remove attachment": "Remover adjunto", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/fr.json b/i18n/fr.json index d5d447e0..10c36a4e 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Télécharger la pièce jointe", "Cloned: '%s'": "Cloner '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Attacher un fichier ", "Remove attachment": "Enlever l'attachement", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/it.json b/i18n/it.json index b7b19e8c..583e0e49 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Scarica Allegato", "Cloned: '%s'": "Copia: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Allega un file", "Remove attachment": "Rimuovi allegato", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/no.json b/i18n/no.json index f131e4fe..c0d376c5 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -130,6 +130,7 @@ "Markdown": "Oppmerket", "Download attachment": "Last ned vedlegg", "Cloned: '%s'": "Kopier: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Legg til fil", "Remove attachment": "Slett vedlegg", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/oc.json b/i18n/oc.json index 8f923b1b..90478768 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Telecargar la pèça junta", "Cloned: '%s'": "Clonar: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Juntar un fichièr ", "Remove attachment": "Levar la pèca junta", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/pl.json b/i18n/pl.json index b722c480..82d9b579 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Pobierz załącznik", "Cloned: '%s'": "Sklonowano: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Załącz plik", "Remove attachment": "Usuń załącznik", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/pt.json b/i18n/pt.json index 01d5dfe8..6af67cf6 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Baixar anexo", "Cloned: '%s'": "Clonado: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Anexar um arquivo", "Remove attachment": "Remover anexo", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/ru.json b/i18n/ru.json index 53431ad2..989d3e4f 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -110,8 +110,6 @@ "неизвестная причина", "server error or not responding": "ошибка сервера или нет ответа", - "unknown error": - "неизвестная ошибка", "Could not post comment: %s": "Не удалось опубликовать комментарий: %s", "Please move your mouse for more entropy…": diff --git a/i18n/sl.json b/i18n/sl.json index 4c0a7e9f..21db8c1d 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Pretoči priponko", "Cloned: '%s'": "'%s' klonirana", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Pripni datoteko", "Remove attachment": "Odstrani priponko", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 187fd1a2..c7ded0ee 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -146,19 +146,31 @@ class I18nTest extends PHPUnit_Framework_TestCase public function testMessageIdsExistInAllLanguages() { $messageIds = array(); - $languages = array(); - $dir = dir(PATH . 'i18n'); + $languages = array(); + $dir = dir(PATH . 'i18n'); while (false !== ($file = $dir->read())) { if (strlen($file) === 7) { - $language = substr($file, 0, 2); - $translations = json_decode(file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), true); - $messageIds = array_unique($messageIds + array_keys($translations)); - $languages[$language] = $translations; + $language = substr($file, 0, 2); + $languageMessageIds = array_keys( + json_decode( + file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), + true + ) + ); + $messageIds = array_unique(array_merge($messageIds, $languageMessageIds)); + $languages[$language] = $languageMessageIds; } } foreach ($messageIds as $messageId) { foreach (array_keys($languages) as $language) { - $this->assertArrayHasKey($messageId, $languages[$language], "message ID '$messageId' exists in translation file $language.json"); + // most languages don't translate the data size units, ignore those + if ($messageId !== 'B' && strlen($messageId) !== 3 && strpos($messageId, 'B', 2) !== 2) { + $this->assertContains( + $messageId, + $languages[$language], + "message ID '$messageId' exists in translation file $language.json" + ); + } } } } From 3fc6ede5bbc32d61826d53ddfe0b478c474148b2 Mon Sep 17 00:00:00 2001 From: Tulio Leao Date: Sat, 25 Mar 2017 09:26:31 -0300 Subject: [PATCH 24/25] Translate missing IDs to Portuguese As referenced on issue #201, some IDs were missing from the pt translation, which were now translated. --- i18n/pt.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/pt.json b/i18n/pt.json index 6af67cf6..05ce23d1 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -130,7 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Baixar anexo", "Cloned: '%s'": "Clonado: '%s'", - "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", + "The cloned file '%s' was attached to this paste.": "O arquivo clonado '%s' foi anexado a essa cópia.", "Attach a file": "Anexar um arquivo", "Remove attachment": "Remover anexo", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -147,9 +147,9 @@ "Enter password": "Digite a senha", "Loading…": "Carregando…", - "Decrypting paste…": "Decrypting paste…", - "Preparing new paste…": "Preparing new paste…", + "Decrypting paste…": "Decifrando cópia…", + "Preparing new paste…": "Preparando nova cópia…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", - "+++ no paste text +++": "+++ no paste text +++" + "+++ no paste text +++": "+++ sem texto de cópia +++" } From 82e45915c73b566a1247c459fcbbd785c783d747 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Sat, 25 Mar 2017 17:06:56 +0300 Subject: [PATCH 25/25] Update Russian translation --- i18n/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index 989d3e4f..da462c38 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -93,7 +93,7 @@ "Anonymous": "Аноним", "Avatar generated from IP address": - "Анонимный аватар (Vizhash IP адреса)", + "Аватар, сгенерированный из IP-адреса", "Add comment": "Добавить комментарий", "Optional nickname…": @@ -105,7 +105,7 @@ "Comment posted.": "Комментарий опубликован.", "Could not refresh display: %s": - "Could not refresh display: %s", + "Не удалось обновить отображение: %s", "unknown status": "неизвестная причина", "server error or not responding":