diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee0909d..189651d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * CHANGED: Drop some PHP < 5.6 fallbacks, minimum version is PHP 7.3 as of release 1.6.0 * CHANGED: Set `lang` cookie with lax `SameSite` property * CHANGED: Upgrading libraries to: DOMpurify 3.1.2 (#1299) & jQuery 3.7.1 +* CHANGED: `create` attribute is no longer returned in API for pastes & can be disabled for comments using `discussiondatedisplay` as well (#1290) * FIXED: Add cache control headers also to API calls (#1263) * FIXED: Shortened paste URL does not appear in email (#606) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 75831f45..31dcb0a8 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -18,6 +18,11 @@ discussion = true ; preselect the discussion feature, defaults to false opendiscussion = false +; enable or disable the diplay of dates & times in the comments, defaults to true +; Note that internally the creation time will still get tracked in order to sort +; the comments by creation time, but you can choose not to display them. +; discussiondatedisplay = false + ; enable or disable the password feature, defaults to true password = true diff --git a/doc/Installation.md b/doc/Installation.md index b09de9e1..d2f21f7d 100644 --- a/doc/Installation.md +++ b/doc/Installation.md @@ -170,14 +170,13 @@ user these additional privileges: For reference or if you want to create the table schema for yourself to avoid having to give PrivateBin too many permissions (replace `prefix_` with your own -table prefix and create the table schema with your favourite MariaDB/MySQL +table prefix and create the table schema with your favorite MariaDB/MySQL client): ```sql CREATE TABLE prefix_paste ( dataid CHAR(16) NOT NULL, data MEDIUMBLOB, - postdate INT, expiredate INT, opendiscussion INT, burnafterreading INT, diff --git a/js/privatebin.js b/js/privatebin.js index e9c0dcde..606ba1bc 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -196,7 +196,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ this.getCreated = function() { - return this.meta[this.v === 1 ? 'postdate' : 'created']; + return this.meta[this.v === 1 ? 'postdate' : 'created'] || 0; } /** @@ -2736,7 +2736,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { if (Editor.isPreview()) { PasteViewer.run(); } - }; /** @@ -3495,9 +3494,11 @@ jQuery.PrivateBin = (function($, RawDeflate) { } // set date + const created = comment.getCreated(); + const commentDate = created == 0 ? '' : ' (' + (new Date(created * 1000).toLocaleString()) + ')'; $commentEntry.find('span.commentdate') - .text(' (' + (new Date(comment.getCreated() * 1000).toLocaleString()) + ')') - .attr('title', 'CommentID: ' + comment.id); + .text(commentDate) + .attr('title', 'CommentID: ' + comment.id); // if an avatar is available, display it const icon = comment.getIcon(); diff --git a/lib/Configuration.php b/lib/Configuration.php index cf0febe6..4ffe4abc 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -40,6 +40,7 @@ class Configuration 'basepath' => '', 'discussion' => true, 'opendiscussion' => false, + 'discussiondatedisplay' => true, 'password' => true, 'fileupload' => false, 'burnafterreadingselected' => false, diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 27874962..b9de2d47 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -146,9 +146,6 @@ class Database extends AbstractData $attachment = $attachmentname = null; $meta = $paste['meta']; $isVersion1 = array_key_exists('data', $paste); - list($createdKey) = $this->_getVersionedKeys($isVersion1 ? 1 : 2); - $created = (int) $meta[$createdKey]; - unset($meta[$createdKey], $paste['meta']); if (array_key_exists('expire_date', $meta)) { $expire_date = (int) $meta['expire_date']; unset($meta['expire_date']); @@ -177,11 +174,10 @@ class Database extends AbstractData try { return $this->_exec( 'INSERT INTO "' . $this->_sanitizeIdentifier('paste') . - '" VALUES(?,?,?,?,?,?,?,?,?)', + '" VALUES(?,?,?,?,?,?,?,?)', array( $pasteid, $isVersion1 ? $paste['data'] : Json::encode($paste), - $created, $expire_date, (int) $opendiscussion, (int) $burnafterreading, @@ -218,13 +214,7 @@ class Database extends AbstractData // create array $data = Json::decode($row['data']); $isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2; - if ($isVersion2) { - $paste = $data; - list($createdKey) = $this->_getVersionedKeys(2); - } else { - $paste = array('data' => $row['data']); - list($createdKey) = $this->_getVersionedKeys(1); - } + $paste = $isVersion2 ? $data : array('data' => $row['data']); try { $row['meta'] = Json::decode($row['meta']); @@ -233,7 +223,6 @@ class Database extends AbstractData } $row = self::upgradePreV1Format($row); $paste['meta'] = $row['meta']; - $paste['meta'][$createdKey] = (int) $row['postdate']; $expire_date = (int) $row['expiredate']; if ($expire_date > 0) { $paste['meta']['expire_date'] = $expire_date; @@ -751,7 +740,6 @@ class Database extends AbstractData 'CREATE TABLE "' . $this->_sanitizeIdentifier('paste') . '" ( ' . "\"dataid\" CHAR(16) NOT NULL$main_key, " . "\"data\" $attachmentType, " . - '"postdate" INT, ' . '"expiredate" INT, ' . '"opendiscussion" INT, ' . '"burnafterreading" INT, ' . @@ -926,6 +914,23 @@ class Database extends AbstractData ); } // no break, continue with updates for all newer versions + case '1.7.2': + $supportsDropColumn = true; + if ($this->_type === 'sqlite') { + try { + $row = $this->_select('SELECT sqlite_version() AS "v"', array(), true); + $supportsDropColumn = version_compare($row['v'], '3.35.0', '>='); + } catch (PDOException $e) { + $supportsDropColumn = false; + } + } + if ($supportsDropColumn) { + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . + '" DROP COLUMN "postdate"' + ); + } + // no break, continue with updates for all newer versions default: $this->_exec( 'UPDATE "' . $this->_sanitizeIdentifier('config') . diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index 8a529c84..c11f8cf6 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -37,7 +37,7 @@ class Paste extends AbstractModel throw new Exception(Controller::GENERIC_ERROR, 64); } - // check if paste has expired and delete it if neccessary. + // check if paste has expired and delete it if necessary. if (array_key_exists('expire_date', $data['meta'])) { if ($data['meta']['expire_date'] < time()) { $this->delete(); @@ -47,6 +47,11 @@ class Paste extends AbstractModel $data['meta']['time_to_live'] = $data['meta']['expire_date'] - time(); unset($data['meta']['expire_date']); } + foreach (array('created', 'postdate') as $key) { + if (array_key_exists($key, $data['meta'])) { + unset($data['meta'][$key]); + } + } // check if non-expired burn after reading paste needs to be deleted if ( @@ -92,8 +97,7 @@ class Paste extends AbstractModel throw new Exception('You are unlucky. Try again.', 75); } - $this->_data['meta']['created'] = time(); - $this->_data['meta']['salt'] = ServerSalt::generate(); + $this->_data['meta']['salt'] = ServerSalt::generate(); // store paste if ( @@ -159,7 +163,17 @@ class Paste extends AbstractModel */ public function getComments() { - return $this->_store->readComments($this->getId()); + if ($this->_conf->getKey('discussiondatedisplay')) { + return $this->_store->readComments($this->getId()); + } + return array_map(function ($comment) { + foreach (array('created', 'postdate') as $key) { + if (array_key_exists($key, $comment['meta'])) { + unset($comment['meta'][$key]); + } + } + return $comment; + }, $this->_store->readComments($this->getId())); } /** diff --git a/lib/Vizhash16x16.php b/lib/Vizhash16x16.php index 2d296af8..5a408655 100644 --- a/lib/Vizhash16x16.php +++ b/lib/Vizhash16x16.php @@ -109,9 +109,9 @@ class Vizhash16x16 for ($i = 0; $i < 7; ++$i) { $action = $this->getInt(); $color = imagecolorallocate($image, $r, $g, $b); - $r = $r0 = ((int) $r0 + $this->getInt() / 25) % 256; - $g = $g0 = ((int) $g0 + $this->getInt() / 25) % 256; - $b = $b0 = ((int) $b0 + $this->getInt() / 25) % 256; + $r = $r0 = (int) ($r0 + $this->getInt() / 25) % 256; + $g = $g0 = (int) ($g0 + $this->getInt() / 25) % 256; + $b = $b0 = (int) ($b0 + $this->getInt() / 25) % 256; $this->drawshape($image, $action, $color); } @@ -148,7 +148,7 @@ class Vizhash16x16 */ private function getX() { - return (int) $this->width * $this->getInt() / 256; + return (int) ($this->width * $this->getInt() / 256); } /** @@ -159,7 +159,7 @@ class Vizhash16x16 */ private function getY() { - return (int) $this->height * $this->getInt() / 256; + return (int) ($this->height * $this->getInt() / 256); } /** @@ -225,8 +225,8 @@ class Vizhash16x16 version_compare(PHP_VERSION, '8.1', '<') ? imagefilledpolygon($image, $points, 4, $color) : imagefilledpolygon($image, $points, $color); break; default: - $start = $this->getInt() * 360 / 256; - $end = $start + $this->getInt() * 180 / 256; + $start = (int) ($this->getInt() * 360 / 256); + $end = (int) ($start + $this->getInt() * 180 / 256); imagefilledarc($image, $this->getX(), $this->getY(), $this->getX(), $this->getY(), $start, $end, $color, IMG_ARC_PIE); } } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index d750b03d..1a05905d 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -73,7 +73,7 @@ endif; ?> - + diff --git a/tpl/bootstrap5.php b/tpl/bootstrap5.php index 116c31c1..5afa4827 100644 --- a/tpl/bootstrap5.php +++ b/tpl/bootstrap5.php @@ -57,7 +57,7 @@ endif; ?> - + diff --git a/tpl/page.php b/tpl/page.php index 12b166ac..a66184f6 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -51,7 +51,7 @@ endif; ?> - + diff --git a/tst/Bootstrap.php b/tst/Bootstrap.php index 183d1b1d..8ea249d2 100644 --- a/tst/Bootstrap.php +++ b/tst/Bootstrap.php @@ -660,7 +660,6 @@ class Helper ), 'meta' => array( 'expire' => '5min', - 'created' => 1344803344, ), 'v' => 2, 'ct' => 'ME5JF/YBEijp2uYMzLZozbKtWc5wfy6R59NBb7SmRig=', diff --git a/tst/ControllerTest.php b/tst/ControllerTest.php index 8aee7829..b1a5774f 100644 --- a/tst/ControllerTest.php +++ b/tst/ControllerTest.php @@ -170,6 +170,7 @@ class ControllerTest extends TestCase $this->assertEquals(0, $response['status'], 'outputs status'); $this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data'); $paste = $this->_data->read($response['id']); + $this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created'); $this->assertEquals( hash_hmac('sha256', $response['id'], $paste['meta']['salt']), $response['deletetoken'], @@ -712,7 +713,7 @@ class ControllerTest extends TestCase $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly'); $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly'); $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly'); - $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly'); + $this->assertFalse(array_key_exists('created', $response['meta']), 'does not output created'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); // by default it will be deleted instantly after it is read @@ -741,7 +742,7 @@ class ControllerTest extends TestCase $this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly'); $this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly'); $this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly'); - $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly'); + $this->assertFalse(array_key_exists('created', $response['meta']), 'does not output created'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); } @@ -771,7 +772,7 @@ class ControllerTest extends TestCase $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste'); $this->assertEquals($paste['data'], $response['data'], 'outputs data correctly'); $this->assertEquals('syntaxhighlighting', $response['meta']['formatter'], 'outputs format correctly'); - $this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly'); + $this->assertFalse(array_key_exists('postdate', $response['meta']), 'does not output postdate'); $this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); diff --git a/tst/Data/DatabaseTest.php b/tst/Data/DatabaseTest.php index 1c5b79de..c903e715 100644 --- a/tst/Data/DatabaseTest.php +++ b/tst/Data/DatabaseTest.php @@ -259,12 +259,11 @@ class DatabaseTest extends TestCase $this->_options['pwd'], $this->_options['opt'] ); - $statement = $db->prepare('INSERT INTO bar_paste VALUES(?,?,?,?,?,?,?,?,?)'); + $statement = $db->prepare('INSERT INTO bar_paste VALUES(?,?,?,?,?,?,?,?)'); $statement->execute( array( Helper::getPasteId(), $paste['data'], - $paste['meta']['postdate'], $paste['meta']['expire_date'], 0, 0, @@ -292,7 +291,7 @@ class DatabaseTest extends TestCase $this->_options['tbl'] = 'baz_'; $model = new Database($this->_options); $paste = Helper::getPaste(1, array('expire_date' => 1344803344)); - unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['salt']); + unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['postdate'], $paste['meta']['salt']); $model->delete(Helper::getPasteId()); $db = new PDO( @@ -301,12 +300,11 @@ class DatabaseTest extends TestCase $this->_options['pwd'], $this->_options['opt'] ); - $statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?,?)'); + $statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?)'); $statement->execute( array( Helper::getPasteId(), $paste['data'], - $paste['meta']['postdate'], $paste['meta']['expire_date'], 0, 0, diff --git a/tst/JsonApiTest.php b/tst/JsonApiTest.php index 032ff4d7..6de7f7bd 100644 --- a/tst/JsonApiTest.php +++ b/tst/JsonApiTest.php @@ -180,7 +180,7 @@ class JsonApiTest extends TestCase $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly'); $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste'); $this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly'); - $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs postdate correctly'); + $this->assertFalse(array_key_exists('created', $paste['meta']), 'does not output created'); $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly'); $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly'); } diff --git a/tst/ModelTest.php b/tst/ModelTest.php index 03855405..16f3fa6e 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -134,12 +134,11 @@ class ModelTest extends TestCase $options['model_options']['pwd'], $options['model_options']['opt'] ); - $statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?,?)'); + $statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?)'); $statement->execute( array( Helper::getPasteId(), $pasteData['data'], - $pasteData['meta']['postdate'], 0, 0, 0, @@ -452,12 +451,13 @@ class ModelTest extends TestCase public function testCommentWithDisabledVizhash() { - $options = parse_ini_file(CONF, true); - $options['main']['icon'] = 'none'; - $options['model'] = array( + $options = parse_ini_file(CONF, true); + $options['main']['discussiondatedisplay'] = 'false'; + $options['main']['icon'] = 'none'; + $options['model'] = array( 'class' => 'Database', ); - $options['model_options'] = array( + $options['model_options'] = array( 'dsn' => 'sqlite::memory:', 'usr' => null, 'pwd' => null, @@ -494,6 +494,10 @@ class ModelTest extends TestCase $comment = current($this->_model->getPaste(Helper::getPasteId())->get()['comments']); $this->assertFalse(array_key_exists('icon', $comment['meta']), 'icon was not generated'); + $this->assertTrue(array_key_exists('created', $comment['meta']), 'creation is set, when using default configuration'); + + $comment = current($paste->get()['comments']); + $this->assertFalse(array_key_exists('created', $comment['meta']), 'creation is not set, if using disabled configuration'); } public function testCommentVizhash()