'));
+ expect(answeringView.model.get('id')).toBe(9);
+ })
+ });
});
diff --git a/frontend/test/views/PostingLiserViewSpec.js b/frontend/test/views/PostingLiserViewSpec.js
new file mode 100644
index 000000000..5cc5741b0
--- /dev/null
+++ b/frontend/test/views/PostingLiserViewSpec.js
@@ -0,0 +1,36 @@
+import PostingSliderView from 'views/PostingSliderView';
+import { PostingModel } from 'modules/posting/models/PostingModel';
+import AnswerModel from 'modules/answering/models/AnswerModel';
+import App from 'models/app';
+
+describe('posting slider', () => {
+ describe('childview sent a new answer', () => {
+ describe('on non-inline form', () => {
+ it('redirects to new posting', () => {
+ const model = new PostingModel();
+ const view = new PostingSliderView({model});
+ const answerModel = new AnswerModel({id: 20});
+
+ App.request.action = 'non-specific-action-triggering-default-route';
+ spyOn(window, 'redirect');
+
+ view.triggerMethod('childview:answering:send:success', answerModel);
+
+ expect(window.redirect).toHaveBeenCalledWith('/test/root/entries/view/20');
+ });
+
+ it('redirects to new mix posting', () => {
+ const model = new PostingModel();
+ const view = new PostingSliderView({model});
+ const answerModel = new AnswerModel({id: 20});
+
+ App.request.action = 'mix';
+ spyOn(window, 'redirect');
+
+ view.triggerMethod('childview:answering:send:success', answerModel);
+
+ expect(window.redirect).toHaveBeenCalledWith('/test/root/entries/mix/20');
+ });
+ });
+ })
+});
diff --git a/karma.conf.js b/karma.conf.js
index df78300d2..195bb8fb3 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -77,11 +77,15 @@ module.exports = function (config) {
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
//browsers: ['Chrome'],
- browsers: ['ChromiumHeadlessCustom'],
+ browsers: ['ChromeHeadlessCustom', 'ChromiumHeadlessCustom'],
customLaunchers: {
ChromiumHeadlessCustom: {
base: 'ChromiumHeadless',
flags: ['--no-sandbox']
+ },
+ ChromeHeadlessCustom: {
+ base: 'ChromeHeadless',
+ flags: ['--no-sandbox']
}
},
diff --git a/package.json b/package.json
index bba3f5ea1..d0939639b 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "Saito",
"private": true,
"dependencies": {
+ "@types/favico.js": "^0.0.28",
"@types/underscore.string": "^0.0.32",
"autosize": "^4.0.2",
"backbone": "^1.4.0",
@@ -12,6 +13,7 @@
"bootstrap": "^4.3.0",
"datatables.net": "^1.10.19",
"datatables.net-bs4": "^1.10.16",
+ "favico.js": "^0.3.10",
"font-awesome": "^4.7.0",
"gettext-extractor": "^3.4.3",
"grunt": "^1.0.4",
@@ -22,7 +24,10 @@
"lodash": "^4.17.10",
"moment": "^2.22.2",
"popper.js": "^1.14.3",
+ "spectrum-colorpicker": "^1.8.0",
"string-template": "^1.0.0",
+ "typeface-cabin": "^0.0.72",
+ "typeface-fenix": "^0.0.72",
"underscore": "~1.9.0",
"underscore.string": "^3.3.4",
"yarn": "^1.15.2"
@@ -40,17 +45,17 @@
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.7.0",
- "cssnano": "^3.10.0",
+ "cssnano": "^4.0.0",
"grunt": "*",
"grunt-concurrent": "*",
"grunt-contrib-clean": "*",
- "grunt-contrib-concat": "^0.5.0",
+ "grunt-contrib-concat": "*",
"grunt-contrib-copy": "*",
- "grunt-contrib-uglify-es": "^3.3.0",
- "grunt-contrib-watch": "^1.0.1",
- "grunt-phpcs": "^0.2.2",
- "grunt-postcss": "^0.9.0",
- "grunt-sass": "^2.1.0",
+ "grunt-contrib-uglify-es": "*",
+ "grunt-contrib-watch": "*",
+ "grunt-phpcs": "*",
+ "grunt-postcss": "*",
+ "grunt-dart-sass": "*",
"grunt-shell": "*",
"jasmine": "^3.1.0",
"jasmine-ajax": "^3.4.0",
@@ -71,7 +76,7 @@
"stylelint-config-standard": "^18.2.0",
"ts-loader": "^4.3.0",
"tslint": "^5.10.0",
- "typescript": "^2.8.3",
+ "typescript": "^3.5.0",
"underscore-template-loader": "^1.0.0",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.1"
@@ -80,10 +85,10 @@
"yarn": ">= 1.0.0"
},
"scripts": {
- "postinstall": "node -e \"try { require('fs').symlinkSync(require('path').resolve('node_modules/@bower_components'), 'bower_components', 'junction') } catch (e) { }\"",
+ "travis": "tslint -c tslint.json \"frontend/src/**/*.ts\"; karma start --single-run --browsers ChromeHeadlessCustom;",
"pretest": "tslint --fix -c tslint.json \"frontend/src/**/*.ts\"",
"test": "karma start --single-run --browsers ChromiumHeadlessCustom",
- "karma": "karma start",
+ "karma": "karma start --browsers ChromiumHeadlessCustom",
"webpack": "webpack --watch"
}
}
diff --git a/phpstan.neon b/phpstan.neon
index 844bbf991..0ff4ae05a 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,6 @@
parameters:
- level: 1
+ level: 3
+ inferPrivatePropertyTypeFromConstructor: true
paths:
- plugins/
- src/
@@ -11,4 +12,9 @@ parameters:
autoload_files:
- tests/bootstrap.php
ignoreErrors:
+ # CakePHP magic find functions
- '#Call to an undefined method.*Table::findBy.*\(\)#'
+ # 3rd party search plugin
+ -
+ message: '#.*searchManager.*#'
+ path: %currentWorkingDirectory%/src/Model/Table/EntriesTable.php
diff --git a/plugins/Admin/config/routes.php b/plugins/Admin/config/routes.php
index 1c326cc55..44214d440 100644
--- a/plugins/Admin/config/routes.php
+++ b/plugins/Admin/config/routes.php
@@ -1,5 +1,15 @@
*/
- $targetCategories = $this->CurrentUser->Categories->getAll(
+ $targetCategories = $this->CurrentUser->getCategories()->getAll(
'read',
'list'
);
diff --git a/plugins/Admin/src/Controller/SettingsController.php b/plugins/Admin/src/Controller/SettingsController.php
index 0ace03bd2..fbd7677da 100755
--- a/plugins/Admin/src/Controller/SettingsController.php
+++ b/plugins/Admin/src/Controller/SettingsController.php
@@ -1,11 +1,11 @@
Settings->patchEntity(
$setting,
$this->request->getData(),
- ['fields' => 'value']
+ ['fields' => ['value']]
);
if ($this->Settings->save($setting)) {
- $this->Flash->set('Saved. @lo', ['element' => 'notice']);
+ // @lo
+ $this->Flash->set('Saved.', ['element' => 'notice']);
return $this->redirect(['action' => 'index', '#' => $id]);
}
- $this->Flash->set('Something went wrong @lo', ['element' => 'error']);
+
+ $errors = $setting->getErrors();
+ // @lo
+ $msg = !empty($errors) ? current(current($errors)) : 'Something went wrong';
+ $this->Flash->set($msg, ['element' => 'error']);
}
$type = $this->settingsShownInAdminIndex[$id]['type'] ?? null;
diff --git a/plugins/Admin/src/Controller/SmileyCodesController.php b/plugins/Admin/src/Controller/SmileyCodesController.php
index e9e0e9e3b..eeb6fdbb9 100644
--- a/plugins/Admin/src/Controller/SmileyCodesController.php
+++ b/plugins/Admin/src/Controller/SmileyCodesController.php
@@ -1,5 +1,15 @@
]http://localhost/img/macnemo.png[/img]
-
### HTML5-Audio ###
@@ -136,17 +124,15 @@ Choose an [appropriate file-format][Video] for your audience.
[Video]: http://en.wikipedia.org/wiki/HTML5_video#Browser_support
-### Iframe & Flash ###
-
-Please use the provided GUI-features.
+### Uploads ###
-### Upload ###
+The BBCode-tag for uploads is generated automatically when a file is inserted through the uploader.
- [upload]filename.ext[/upload]
+### Other External Content ###
-### Embed.ly ###
+ [embed]http://example.com/content.something[/embed]
-If activated `[embed]
[/embed]` tries to embed the URL via [embed.ly](http://embed.ly/).
+Tries to embed the content, a short extract of the content or a apppropriate link.
## Layout ##
@@ -154,4 +140,4 @@ If activated `[embed][/embed]` tries to embed the URL via [embed.ly](http:/
[float]content[/float]
-Floats the content to the side.
\ No newline at end of file
+Floats the content to the side.
diff --git a/plugins/BbcodeParser/src/Lib/Editor.php b/plugins/BbcodeParser/src/Lib/Editor.php
index 1aeb2e916..ea6f8236f 100644
--- a/plugins/BbcodeParser/src/Lib/Editor.php
+++ b/plugins/BbcodeParser/src/Lib/Editor.php
@@ -1,11 +1,11 @@
getParent()->getTagName() === 'url') {
return $string;
}
- $string = $this->_hashLink($string);
- $string = $this->_atUserLink($string);
+ $string = $this->hashLink($string);
+ $string = $this->atUserLink($string);
- return $this->_autolink($string);
+ return $this->autolink($string);
}
/**
- * {@inheritDoc}
+ * Links @ to the user's profile.
+ *
+ * @param string $string The Text to be parsed.
+ * @return string The text with usernames linked.
*/
- protected function _atUserLink($string)
+ protected function atUserLink(string $string): string
{
$tags = [];
@@ -84,15 +100,19 @@ protected function _atUserLink($string)
}
/**
- * autolink
+ * Autolinks URLs not surrounded by explicit URL-tags for user-convenience.
*
- * @param string $string string
- *
- * @return string
+ * @param string $string The text to be parsed for URLs.
+ * @return string The text with URLs linked.
*/
- protected function _autolink($string)
+ protected function autolink(string $string): string
{
- $replace = function ($matches) {
+ $replace = function (array $matches): string {
+ // don't link locally
+ if (strpos($matches['element'], 'file://') !== false) {
+ return $matches['element'];
+ }
+
// exclude punctuation at end of sentence from URLs
$ignoredEndChars = implode('|', [',', '\?', ',', '\.', '\)', '!']);
preg_match(
@@ -100,8 +120,8 @@ protected function _autolink($string)
$matches['element'],
$m
);
- // keep ['element'] and ['suffix'] and include ['prefix']
- $matches = $m + $matches;
+ // keep ['element'] and ['suffix'] and include ['prefix']; (array) for phpstan
+ $matches = (array)($m + $matches);
if (strpos($matches['element'], '://') === false) {
$matches['element'] = 'http://' . $matches['element'];
@@ -148,18 +168,17 @@ function ($matches) {
}
/**
- * Hash link
- *
- * @param string $string string
+ * Links # to that posting.
*
- * @return string
+ * @param string $string Text to be parsed for #.
+ * @return string Text containing hash-links.
*/
- protected function _hashLink($string)
+ protected function hashLink(string $string): string
{
$baseUrl = $this->_sOptions->get('webroot') . $this->_sOptions->get('hashBaseUrl');
$string = preg_replace_callback(
- '/(?<=\s|^|])(?#)(?\d+)(?!\w)/',
- function ($m) use ($baseUrl) {
+ '/(?<=\s|^|]|\()(?#)(?\d+)(?!\w)/',
+ function (array $m) use ($baseUrl): string {
$hash = $m['element'];
return $this->_url($baseUrl . $hash, '#' . $hash);
diff --git a/plugins/BbcodeParser/src/Lib/jBBCode/Visitors/JbbCodeNl2BrVisitor.php b/plugins/BbcodeParser/src/Lib/jBBCode/Visitors/JbbCodeNl2BrVisitor.php
index deebdb376..81397b71a 100644
--- a/plugins/BbcodeParser/src/Lib/jBBCode/Visitors/JbbCodeNl2BrVisitor.php
+++ b/plugins/BbcodeParser/src/Lib/jBBCode/Visitors/JbbCodeNl2BrVisitor.php
@@ -1,5 +1,15 @@
_Parser->parse($input);
$this->assertHtml($expected, $result);
+
+ /// in paranthesis
+ $input = "foo (#2234) bar";
+ $expected = [
+ 'foo (',
+ 'a' => [
+ 'href' => '/hash/2234'
+ ],
+ '#2234',
+ '/a',
+ ') bar'
+ ];
+ $result = $this->_Parser->parse($input);
+ $this->assertHtml($expected, $result);
}
public function testHashLinkFailure()
@@ -606,6 +630,13 @@ public function testLinkAutoSurroundingChars()
$this->assertHtml($expected, $result);
}
+ public function testLinkAutoIgnoreLocalFiles()
+ {
+ $input = 'a file:///foo.bar b file://foo c file:// d file:///';
+ $result = $this->_Parser->parse($input);
+ $this->assertEquals($input, $result);
+ }
+
public function testReturnText()
{
$in = 'test [b]test[b] test';
diff --git a/plugins/BbcodeParser/tests/TestCase/Lib/MarkupTest.php b/plugins/BbcodeParser/tests/TestCase/Lib/MarkupTest.php
index 45b416be3..bd2a26355 100644
--- a/plugins/BbcodeParser/tests/TestCase/Lib/MarkupTest.php
+++ b/plugins/BbcodeParser/tests/TestCase/Lib/MarkupTest.php
@@ -1,5 +1,15 @@
CurrentUser->Categories->getAll('read');
+ $categories = $this->CurrentUser->getCategories()->getAll('read');
$bookmarks = $this->Bookmarks->find(
'all',
[
@@ -137,9 +135,9 @@ public function beforeFilter(Event $event)
* @param int $bookmarkId bookmark-ID
* @throws NotFoundException
* @throws SaitoForbiddenException
- * @return Entity
+ * @return EntityInterface
*/
- private function getBookmark(int $bookmarkId): Entity
+ private function getBookmark(int $bookmarkId): EntityInterface
{
$bookmark = $this->Bookmarks->find()
->where(['id' => $bookmarkId])
diff --git a/plugins/Bookmarks/src/Lib/Bookmarks.php b/plugins/Bookmarks/src/Lib/Bookmarks.php
index ea3662b3f..a33e2e8fa 100644
--- a/plugins/Bookmarks/src/Lib/Bookmarks.php
+++ b/plugins/Bookmarks/src/Lib/Bookmarks.php
@@ -1,18 +1,18 @@
id, …]
+ * @var array format: [entry_id => id, …]
*/
protected $_bookmarks;
@@ -69,7 +69,9 @@ protected function _load()
return;
}
- $this->_bookmarks = TableRegistry::get('Bookmarks.Bookmarks')
+ /** @var BookmarksTable */
+ $BookmarksTable = TableRegistry::get('Bookmarks.Bookmarks');
+ $this->_bookmarks = $BookmarksTable
->find('list', ['keyField' => 'entry_id', 'valueField' => 'id'])
->where(['user_id' => $this->_CurrentUser->getId()])
->toArray();
diff --git a/plugins/Bookmarks/src/Model/Table/BookmarksTable.php b/plugins/Bookmarks/src/Model/Table/BookmarksTable.php
index 3a8017cfc..e942a8849 100644
--- a/plugins/Bookmarks/src/Model/Table/BookmarksTable.php
+++ b/plugins/Bookmarks/src/Model/Table/BookmarksTable.php
@@ -1,11 +1,11 @@
extend('_default');
$this->start('theme_head');
?>
-
-
+