mirror of
https://github.com/flarum/core.git
synced 2025-07-16 14:26:25 +02:00
Merge remote-tracking branch 'extensions_sticky/REWRITE'
This commit is contained in:
19
extensions/sticky/.editorconfig
Normal file
19
extensions/sticky/.editorconfig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{diff,md}]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{php,xml,json}]
|
||||||
|
indent_size = 4
|
18
extensions/sticky/.gitattributes
vendored
Normal file
18
extensions/sticky/.gitattributes
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
||||||
|
.gitmodules export-ignore
|
||||||
|
.github export-ignore
|
||||||
|
.travis export-ignore
|
||||||
|
.travis.yml export-ignore
|
||||||
|
.editorconfig export-ignore
|
||||||
|
.styleci.yml export-ignore
|
||||||
|
|
||||||
|
phpunit.xml export-ignore
|
||||||
|
tests export-ignore
|
||||||
|
|
||||||
|
js/dist/* -diff
|
||||||
|
js/dist/* linguist-generated
|
||||||
|
js/dist-typings/* linguist-generated
|
||||||
|
js/yarn.lock -diff
|
||||||
|
|
||||||
|
* text=auto eol=lf
|
15
extensions/sticky/.github/workflows/backend.yml
vendored
Normal file
15
extensions/sticky/.github/workflows/backend.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Sticky PHP
|
||||||
|
|
||||||
|
on: [workflow_dispatch, push, pull_request]
|
||||||
|
|
||||||
|
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
||||||
|
# This will break your current script.
|
||||||
|
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
uses: flarum/.github/.github/workflows/REUSABLE_backend.yml@main
|
||||||
|
with:
|
||||||
|
enable_backend_testing: false
|
||||||
|
|
||||||
|
backend_directory: .
|
21
extensions/sticky/.github/workflows/frontend.yml
vendored
Normal file
21
extensions/sticky/.github/workflows/frontend.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Sticky JS
|
||||||
|
|
||||||
|
on: [workflow_dispatch, push, pull_request]
|
||||||
|
|
||||||
|
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
||||||
|
# This will break your current script.
|
||||||
|
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
uses: flarum/.github/.github/workflows/REUSABLE_frontend.yml@main
|
||||||
|
with:
|
||||||
|
enable_bundlewatch: false
|
||||||
|
enable_prettier: true
|
||||||
|
enable_typescript: false
|
||||||
|
|
||||||
|
frontend_directory: ./js
|
||||||
|
main_git_branch: master
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
|
12
extensions/sticky/.gitignore
vendored
Normal file
12
extensions/sticky/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/vendor
|
||||||
|
composer.lock
|
||||||
|
composer.phar
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
tests/.phpunit.result.cache
|
||||||
|
/tests/integration/tmp
|
||||||
|
.vagrant
|
||||||
|
.idea/*
|
||||||
|
.vscode
|
||||||
|
js/coverage-ts
|
14
extensions/sticky/.styleci.yml
Normal file
14
extensions/sticky/.styleci.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
preset: recommended
|
||||||
|
|
||||||
|
enabled:
|
||||||
|
- logical_not_operators_with_successor_space
|
||||||
|
|
||||||
|
disabled:
|
||||||
|
- align_double_arrow
|
||||||
|
- blank_line_after_opening_tag
|
||||||
|
- multiline_array_trailing_comma
|
||||||
|
- new_with_braces
|
||||||
|
- phpdoc_align
|
||||||
|
- phpdoc_order
|
||||||
|
- phpdoc_separation
|
||||||
|
- phpdoc_types
|
63
extensions/sticky/CHANGELOG.md
Normal file
63
extensions/sticky/CHANGELOG.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.2.0](https://github.com/flarum/sticky/compare/v1.1.0...v1.2.0)
|
||||||
|
|
||||||
|
No changes.
|
||||||
|
|
||||||
|
## [1.1.0](https://github.com/flarum/sticky/compare/v1.0.0...v1.1.0)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Classes to sticky discussions (https://github.com/flarum/sticky/pulls/31)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Use css variables for badge (https://github.com/flarum/sticky/pulls/29)
|
||||||
|
- Repo maintenance (https://github.com/flarum/sticky/pulls/30)
|
||||||
|
|
||||||
|
## [1.0.0](https://github.com/flarum/sticky/compare/v0.1.0-beta.16...v1.0.0)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Compatibility with Flarum v1.0.0.
|
||||||
|
- List controller now takes into account default sort, even if it is changed from an extension
|
||||||
|
|
||||||
|
## [0.1.0-beta.16](https://github.com/flarum/sticky/compare/v0.1.0-beta.15.1...v0.1.0-beta.16)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated admin category from moderation to feature (https://github.com/flarum/sticky/pull/27)
|
||||||
|
- Moved locale files from translation pack to extension (https://github.com/flarum/sticky/pull/21)
|
||||||
|
- Make `is_sticky` and `last_posted_at` columns an index for improved performance (https://github.com/flarum/sticky/pull/23)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Rendering of excerpts failing (https://github.com/flarum/sticky/pull/24)
|
||||||
|
- Excerpts showing on user profile page for stickied discussions looks erratic (https://github.com/flarum/sticky/pull/25)
|
||||||
|
|
||||||
|
## [0.1.0-beta.15.1](https://github.com/flarum/sticky/compare/v0.1.0-beta.14...v0.1.0-beta.15.1)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed security vulnerability for HTML injection in the first post of stickied discussions. See https://github.com/flarum/sticky/security/advisories/GHSA-h3gg-7wx2-cq3h.
|
||||||
|
|
||||||
|
## [0.1.0-beta.15](https://github.com/flarum/sticky/compare/v0.1.0-beta.14...v0.1.0-beta.15)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated composer.json and admin javascript for new admin area.
|
||||||
|
- Updated to use newest extenders.
|
||||||
|
|
||||||
|
## [0.1.0-beta.14](https://github.com/flarum/sticky/compare/v0.1.0-beta.13...v0.1.0-beta.14)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated mithril to version 2
|
||||||
|
- Load language strings correctly on en-/disable
|
||||||
|
- Updated JS dependencies
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Excerpts of unread stickied discussions triggers an error (#20)
|
||||||
|
|
||||||
|
## [0.1.0-beta.13](https://github.com/flarum/sticky/compare/v0.1.0-beta.12...v0.1.0-beta.13)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated JS dependencies
|
||||||
|
|
||||||
|
## [0.1.0-beta.9](https://github.com/flarum/sticky/compare/v0.1.0-beta.8...v0.1.0-beta.9)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Compatibility with Laravel 5.7 ([196b2d1](https://github.com/flarum/sticky/commit/196b2d1bfd59ccbe440b4a56bf28f9d17e3d120e))
|
||||||
|
|
22
extensions/sticky/LICENSE
Normal file
22
extensions/sticky/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019-2021 Stichting Flarum (Flarum Foundation)
|
||||||
|
Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
60
extensions/sticky/composer.json
Normal file
60
extensions/sticky/composer.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"name": "flarum/sticky",
|
||||||
|
"description": "Pin discussions to the top of the list.",
|
||||||
|
"type": "flarum-extension",
|
||||||
|
"keywords": [
|
||||||
|
"discussion"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/flarum/core/issues",
|
||||||
|
"source": "https://github.com/flarum/sticky",
|
||||||
|
"forum": "https://discuss.flarum.org"
|
||||||
|
},
|
||||||
|
"homepage": "https://flarum.org",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "website",
|
||||||
|
"url": "https://flarum.org/donate/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"flarum/core": "^1.2"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flarum\\Sticky\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.x-dev"
|
||||||
|
},
|
||||||
|
"flarum-extension": {
|
||||||
|
"title": "Sticky",
|
||||||
|
"category": "feature",
|
||||||
|
"icon": {
|
||||||
|
"name": "fas fa-thumbtack",
|
||||||
|
"backgroundColor": "#D13E32",
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flarum-cli": {
|
||||||
|
"modules": {
|
||||||
|
"admin": true,
|
||||||
|
"forum": true,
|
||||||
|
"js": true,
|
||||||
|
"jsCommon": false,
|
||||||
|
"css": true,
|
||||||
|
"gitConf": true,
|
||||||
|
"githubActions": true,
|
||||||
|
"prettier": true,
|
||||||
|
"typescript": false,
|
||||||
|
"bundlewatch": false,
|
||||||
|
"backendTesting": false,
|
||||||
|
"editorConfig": true,
|
||||||
|
"styleci": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
extensions/sticky/extend.php
Normal file
59
extensions/sticky/extend.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Api\Controller\ListDiscussionsController;
|
||||||
|
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||||
|
use Flarum\Discussion\Event\Saving;
|
||||||
|
use Flarum\Discussion\Filter\DiscussionFilterer;
|
||||||
|
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasStickied;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasUnstickied;
|
||||||
|
use Flarum\Sticky\Listener;
|
||||||
|
use Flarum\Sticky\Listener\SaveStickyToDatabase;
|
||||||
|
use Flarum\Sticky\PinStickiedDiscussionsToTop;
|
||||||
|
use Flarum\Sticky\Post\DiscussionStickiedPost;
|
||||||
|
use Flarum\Sticky\Query\StickyFilterGambit;
|
||||||
|
|
||||||
|
return [
|
||||||
|
(new Extend\Frontend('forum'))
|
||||||
|
->js(__DIR__.'/js/dist/forum.js')
|
||||||
|
->css(__DIR__.'/less/forum.less'),
|
||||||
|
|
||||||
|
(new Extend\Post())
|
||||||
|
->type(DiscussionStickiedPost::class),
|
||||||
|
|
||||||
|
(new Extend\ApiSerializer(DiscussionSerializer::class))
|
||||||
|
->attribute('isSticky', function (DiscussionSerializer $serializer, $discussion) {
|
||||||
|
return (bool) $discussion->is_sticky;
|
||||||
|
})
|
||||||
|
->attribute('canSticky', function (DiscussionSerializer $serializer, $discussion) {
|
||||||
|
return (bool) $serializer->getActor()->can('sticky', $discussion);
|
||||||
|
}),
|
||||||
|
|
||||||
|
(new Extend\ApiController(ListDiscussionsController::class))
|
||||||
|
->addInclude('firstPost'),
|
||||||
|
|
||||||
|
(new Extend\Frontend('admin'))
|
||||||
|
->js(__DIR__.'/js/dist/admin.js'),
|
||||||
|
|
||||||
|
new Extend\Locales(__DIR__.'/locale'),
|
||||||
|
|
||||||
|
(new Extend\Event())
|
||||||
|
->listen(Saving::class, SaveStickyToDatabase::class)
|
||||||
|
->listen(DiscussionWasStickied::class, [Listener\CreatePostWhenDiscussionIsStickied::class, 'whenDiscussionWasStickied'])
|
||||||
|
->listen(DiscussionWasUnstickied::class, [Listener\CreatePostWhenDiscussionIsStickied::class, 'whenDiscussionWasUnstickied']),
|
||||||
|
|
||||||
|
(new Extend\Filter(DiscussionFilterer::class))
|
||||||
|
->addFilter(StickyFilterGambit::class)
|
||||||
|
->addFilterMutator(PinStickiedDiscussionsToTop::class),
|
||||||
|
|
||||||
|
(new Extend\SimpleFlarumSearch(DiscussionSearcher::class))
|
||||||
|
->addGambit(StickyFilterGambit::class),
|
||||||
|
];
|
9
extensions/sticky/js/.gitignore
vendored
Normal file
9
extensions/sticky/js/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
node_modules
|
1
extensions/sticky/js/admin.js
Normal file
1
extensions/sticky/js/admin.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './src/admin';
|
2
extensions/sticky/js/dist/admin.js
generated
vendored
Normal file
2
extensions/sticky/js/dist/admin.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
(()=>{var e={n:r=>{var t=r&&r.__esModule?()=>r.default:()=>r;return e.d(t,{a:t}),t},d:(r,t)=>{for(var o in t)e.o(t,o)&&!e.o(r,o)&&Object.defineProperty(r,o,{enumerable:!0,get:t[o]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};(()=>{"use strict";e.r(r);const t=flarum.core.compat["admin/app"];var o=e.n(t);o().initializers.add("flarum-sticky",(function(){o().extensionData.for("flarum-sticky").registerPermission({icon:"fas fa-thumbtack",label:o().translator.trans("flarum-sticky.admin.permissions.sticky_discussions_label"),permission:"discussion.sticky"},"moderate",95)}))})(),module.exports=r})();
|
||||||
|
//# sourceMappingURL=admin.js.map
|
1
extensions/sticky/js/dist/admin.js.map
generated
vendored
Normal file
1
extensions/sticky/js/dist/admin.js.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,iBAAiB,WACpCA,IAAAA,cAAAA,IAAsB,iBAAiBC,mBACrC,CACEC,KAAM,mBACNC,MAAOH,IAAAA,WAAAA,MAAqB,4DAC5BI,WAAY,qBAEd,WACA,Q","sources":["webpack://@flarum/sticky/webpack/bootstrap","webpack://@flarum/sticky/webpack/runtime/compat get default export","webpack://@flarum/sticky/webpack/runtime/define property getters","webpack://@flarum/sticky/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/sticky/webpack/runtime/make namespace object","webpack://@flarum/sticky/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/sticky/./src/admin/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('flarum-sticky', () => {\n app.extensionData.for('flarum-sticky').registerPermission(\n {\n icon: 'fas fa-thumbtack',\n label: app.translator.trans('flarum-sticky.admin.permissions.sticky_discussions_label'),\n permission: 'discussion.sticky',\n },\n 'moderate',\n 95\n );\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","registerPermission","icon","label","permission"],"sourceRoot":""}
|
2
extensions/sticky/js/dist/forum.js
generated
vendored
Normal file
2
extensions/sticky/js/dist/forum.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
(()=>{var t={n:o=>{var s=o&&o.__esModule?()=>o.default:()=>o;return t.d(s,{a:s}),s},d:(o,s)=>{for(var n in s)t.o(s,n)&&!t.o(o,n)&&Object.defineProperty(o,n,{enumerable:!0,get:s[n]})},o:(t,o)=>Object.prototype.hasOwnProperty.call(t,o),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},o={};(()=>{"use strict";t.r(o);const s=flarum.core.compat["forum/app"];var n=t.n(s);const c=flarum.core.compat["common/Model"];var r=t.n(c);const e=flarum.core.compat["common/models/Discussion"];var i=t.n(e);function a(t,o){return a=Object.setPrototypeOf||function(t,o){return t.__proto__=o,t},a(t,o)}const u=flarum.core.compat["forum/components/EventPost"];var p=function(t){var o,s;function n(){return t.apply(this,arguments)||this}s=t,(o=n).prototype=Object.create(s.prototype),o.prototype.constructor=o,a(o,s);var c=n.prototype;return c.icon=function(){return"fas fa-thumbtack"},c.descriptionKey=function(){return this.attrs.post.content().sticky?"flarum-sticky.forum.post_stream.discussion_stickied_text":"flarum-sticky.forum.post_stream.discussion_unstickied_text"},n}(t.n(u)());const f=flarum.core.compat["common/extend"],l=flarum.core.compat["common/components/Badge"];var d=t.n(l);const y=flarum.core.compat["forum/utils/DiscussionControls"];var k=t.n(y);const v=flarum.core.compat["forum/components/DiscussionPage"];var b=t.n(v);const h=flarum.core.compat["common/components/Button"];var _=t.n(h);const S=flarum.core.compat["forum/states/DiscussionListState"];var P=t.n(S);const g=flarum.core.compat["forum/components/DiscussionListItem"];var x=t.n(g);const O=flarum.core.compat["forum/components/IndexPage"];var j=t.n(O);const D=flarum.core.compat["common/utils/string"],I=flarum.core.compat["common/utils/classList"];var L=t.n(I);n().initializers.add("flarum-sticky",(function(){n().postComponents.discussionStickied=p,i().prototype.isSticky=r().attribute("isSticky"),i().prototype.canSticky=r().attribute("canSticky"),(0,f.extend)(i().prototype,"badges",(function(t){this.isSticky()&&t.add("sticky",d().component({type:"sticky",label:n().translator.trans("flarum-sticky.forum.badge.sticky_tooltip"),icon:"fas fa-thumbtack"}),10)})),(0,f.extend)(k(),"moderationControls",(function(t,o){o.canSticky()&&t.add("sticky",_().component({icon:"fas fa-thumbtack",onclick:this.stickyAction.bind(o)},n().translator.trans(o.isSticky()?"flarum-sticky.forum.discussion_controls.unsticky_button":"flarum-sticky.forum.discussion_controls.sticky_button")))})),k().stickyAction=function(){this.save({isSticky:!this.isSticky()}).then((function(){n().current.matches(b())&&n().current.get("stream").update(),m.redraw()}))},(0,f.extend)(P().prototype,"requestParams",(function(t){(n().current.matches(j())||n().current.matches(b()))&&t.include.push("firstPost")})),(0,f.extend)(x().prototype,"infoItems",(function(t){var o=this.attrs.discussion;if(o.isSticky()&&!this.attrs.params.q&&!o.lastReadPostNumber()){var s=o.firstPost();if(s){var n=(0,D.truncate)(s.contentPlain(),175);t.add("excerpt",m("div",null,n),-100)}}})),(0,f.extend)(x().prototype,"elementAttrs",(function(t){this.attrs.discussion.isSticky()&&(t.className=L()(t.className,"DiscussionListItem--sticky"))}))}))})(),module.exports=o})();
|
||||||
|
//# sourceMappingURL=forum.js.map
|
1
extensions/sticky/js/dist/forum.js.map
generated
vendored
Normal file
1
extensions/sticky/js/dist/forum.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/sticky/js/forum.js
Normal file
1
extensions/sticky/js/forum.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './src/forum';
|
19
extensions/sticky/js/package.json
Normal file
19
extensions/sticky/js/package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@flarum/sticky",
|
||||||
|
"prettier": "@flarum/prettier-config",
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"flarum-webpack-config": "^2.0.0",
|
||||||
|
"webpack": "^5.65.0",
|
||||||
|
"webpack-cli": "^4.9.1",
|
||||||
|
"@flarum/prettier-config": "^1.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack --mode development --watch",
|
||||||
|
"build": "webpack --mode production",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"format-check": "prettier --check src",
|
||||||
|
"analyze": "cross-env ANALYZER=true yarn build"
|
||||||
|
}
|
||||||
|
}
|
13
extensions/sticky/js/src/admin/index.js
Normal file
13
extensions/sticky/js/src/admin/index.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import app from 'flarum/admin/app';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-sticky', () => {
|
||||||
|
app.extensionData.for('flarum-sticky').registerPermission(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-thumbtack',
|
||||||
|
label: app.translator.trans('flarum-sticky.admin.permissions.sticky_discussions_label'),
|
||||||
|
permission: 'discussion.sticky',
|
||||||
|
},
|
||||||
|
'moderate',
|
||||||
|
95
|
||||||
|
);
|
||||||
|
});
|
20
extensions/sticky/js/src/forum/addStickyBadge.js
Normal file
20
extensions/sticky/js/src/forum/addStickyBadge.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import Discussion from 'flarum/common/models/Discussion';
|
||||||
|
import Badge from 'flarum/common/components/Badge';
|
||||||
|
|
||||||
|
export default function addStickyBadge() {
|
||||||
|
extend(Discussion.prototype, 'badges', function (badges) {
|
||||||
|
if (this.isSticky()) {
|
||||||
|
badges.add(
|
||||||
|
'sticky',
|
||||||
|
Badge.component({
|
||||||
|
type: 'sticky',
|
||||||
|
label: app.translator.trans('flarum-sticky.forum.badge.sticky_tooltip'),
|
||||||
|
icon: 'fas fa-thumbtack',
|
||||||
|
}),
|
||||||
|
10
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
12
extensions/sticky/js/src/forum/addStickyClass.js
Normal file
12
extensions/sticky/js/src/forum/addStickyClass.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import classList from 'flarum/common/utils/classList';
|
||||||
|
|
||||||
|
import DiscussionListItem from 'flarum/forum/components/DiscussionListItem';
|
||||||
|
|
||||||
|
export default function addStickyClass() {
|
||||||
|
extend(DiscussionListItem.prototype, 'elementAttrs', function (attrs) {
|
||||||
|
if (this.attrs.discussion.isSticky()) {
|
||||||
|
attrs.className = classList(attrs.className, 'DiscussionListItem--sticky');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
36
extensions/sticky/js/src/forum/addStickyControl.js
Normal file
36
extensions/sticky/js/src/forum/addStickyControl.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import DiscussionControls from 'flarum/forum/utils/DiscussionControls';
|
||||||
|
import DiscussionPage from 'flarum/forum/components/DiscussionPage';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
|
||||||
|
export default function addStickyControl() {
|
||||||
|
extend(DiscussionControls, 'moderationControls', function (items, discussion) {
|
||||||
|
if (discussion.canSticky()) {
|
||||||
|
items.add(
|
||||||
|
'sticky',
|
||||||
|
Button.component(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-thumbtack',
|
||||||
|
onclick: this.stickyAction.bind(discussion),
|
||||||
|
},
|
||||||
|
app.translator.trans(
|
||||||
|
discussion.isSticky()
|
||||||
|
? 'flarum-sticky.forum.discussion_controls.unsticky_button'
|
||||||
|
: 'flarum-sticky.forum.discussion_controls.sticky_button'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
DiscussionControls.stickyAction = function () {
|
||||||
|
this.save({ isSticky: !this.isSticky() }).then(() => {
|
||||||
|
if (app.current.matches(DiscussionPage)) {
|
||||||
|
app.current.get('stream').update();
|
||||||
|
}
|
||||||
|
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
30
extensions/sticky/js/src/forum/addStickyExcerpt.js
Normal file
30
extensions/sticky/js/src/forum/addStickyExcerpt.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import DiscussionListState from 'flarum/forum/states/DiscussionListState';
|
||||||
|
import DiscussionListItem from 'flarum/forum/components/DiscussionListItem';
|
||||||
|
import DiscussionPage from 'flarum/forum/components/DiscussionPage';
|
||||||
|
import IndexPage from 'flarum/forum/components/IndexPage';
|
||||||
|
import { truncate } from 'flarum/common/utils/string';
|
||||||
|
|
||||||
|
export default function addStickyControl() {
|
||||||
|
extend(DiscussionListState.prototype, 'requestParams', function (params) {
|
||||||
|
if (app.current.matches(IndexPage) || app.current.matches(DiscussionPage)) {
|
||||||
|
params.include.push('firstPost');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extend(DiscussionListItem.prototype, 'infoItems', function (items) {
|
||||||
|
const discussion = this.attrs.discussion;
|
||||||
|
|
||||||
|
if (discussion.isSticky() && !this.attrs.params.q && !discussion.lastReadPostNumber()) {
|
||||||
|
const firstPost = discussion.firstPost();
|
||||||
|
|
||||||
|
if (firstPost) {
|
||||||
|
const excerpt = truncate(firstPost.contentPlain(), 175);
|
||||||
|
|
||||||
|
// Wrapping in <div> because ItemList entries need to be vnodes
|
||||||
|
items.add('excerpt', <div>{excerpt}</div>, -100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import EventPost from 'flarum/forum/components/EventPost';
|
||||||
|
|
||||||
|
export default class DiscussionStickiedPost extends EventPost {
|
||||||
|
icon() {
|
||||||
|
return 'fas fa-thumbtack';
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionKey() {
|
||||||
|
return this.attrs.post.content().sticky
|
||||||
|
? 'flarum-sticky.forum.post_stream.discussion_stickied_text'
|
||||||
|
: 'flarum-sticky.forum.post_stream.discussion_unstickied_text';
|
||||||
|
}
|
||||||
|
}
|
21
extensions/sticky/js/src/forum/index.js
Normal file
21
extensions/sticky/js/src/forum/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Model from 'flarum/common/Model';
|
||||||
|
import Discussion from 'flarum/common/models/Discussion';
|
||||||
|
|
||||||
|
import DiscussionStickiedPost from './components/DiscussionStickiedPost';
|
||||||
|
import addStickyBadge from './addStickyBadge';
|
||||||
|
import addStickyControl from './addStickyControl';
|
||||||
|
import addStickyExcerpt from './addStickyExcerpt';
|
||||||
|
import addStickyClass from './addStickyClass';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-sticky', () => {
|
||||||
|
app.postComponents.discussionStickied = DiscussionStickiedPost;
|
||||||
|
|
||||||
|
Discussion.prototype.isSticky = Model.attribute('isSticky');
|
||||||
|
Discussion.prototype.canSticky = Model.attribute('canSticky');
|
||||||
|
|
||||||
|
addStickyBadge();
|
||||||
|
addStickyControl();
|
||||||
|
addStickyExcerpt();
|
||||||
|
addStickyClass();
|
||||||
|
});
|
1
extensions/sticky/js/webpack.config.js
Normal file
1
extensions/sticky/js/webpack.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('flarum-webpack-config')();
|
2284
extensions/sticky/js/yarn.lock
Normal file
2284
extensions/sticky/js/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
0
extensions/sticky/less/admin.less
Normal file
0
extensions/sticky/less/admin.less
Normal file
15
extensions/sticky/less/forum.less
Normal file
15
extensions/sticky/less/forum.less
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
:root {
|
||||||
|
--sticky-bg: #d13e32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Badge--sticky {
|
||||||
|
--badge-bg: var(--sticky-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.DiscussionStickiedPost {
|
||||||
|
& .EventPost-icon,
|
||||||
|
& .EventPost-info,
|
||||||
|
& .EventPost-info a {
|
||||||
|
color: var(--sticky-bg);
|
||||||
|
}
|
||||||
|
}
|
37
extensions/sticky/locale/en.yml
Normal file
37
extensions/sticky/locale/en.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
flarum-sticky:
|
||||||
|
|
||||||
|
##
|
||||||
|
# UNIQUE KEYS - The following keys are used in only one location each.
|
||||||
|
##
|
||||||
|
|
||||||
|
# Translations in this namespace are used by the admin interface.
|
||||||
|
admin:
|
||||||
|
|
||||||
|
# These translations are used in the Permissions page of the admin interface.
|
||||||
|
permissions:
|
||||||
|
sticky_discussions_label: Sticky discussions
|
||||||
|
|
||||||
|
# Translations in this namespace are used by the forum user interface.
|
||||||
|
forum:
|
||||||
|
|
||||||
|
# These translations are displayed as tooltips for discussion badges.
|
||||||
|
badge:
|
||||||
|
sticky_tooltip: => flarum-sticky.ref.sticky
|
||||||
|
|
||||||
|
# These translations are used by the discussion control buttons.
|
||||||
|
discussion_controls:
|
||||||
|
sticky_button: => flarum-sticky.ref.sticky
|
||||||
|
unsticky_button: Unsticky
|
||||||
|
|
||||||
|
# These translations are displayed between posts in the post stream.
|
||||||
|
post_stream:
|
||||||
|
discussion_stickied_text: "{username} stickied the discussion."
|
||||||
|
discussion_unstickied_text: "{username} unstickied the discussion."
|
||||||
|
|
||||||
|
##
|
||||||
|
# REUSED TRANSLATIONS - These keys should not be used directly in code!
|
||||||
|
##
|
||||||
|
|
||||||
|
# Translations in this namespace are referenced by two or more unique keys.
|
||||||
|
ref:
|
||||||
|
sticky: Sticky
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
|
||||||
|
return Migration::addColumns('discussions', [
|
||||||
|
'is_sticky' => ['boolean', 'default' => 0]
|
||||||
|
]);
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
|
||||||
|
return Migration::addPermissions([
|
||||||
|
'discussion.sticky' => Group::MODERATOR_ID
|
||||||
|
]);
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'up' => function (Builder $schema) {
|
||||||
|
$schema->table('discussions', function (Blueprint $table) {
|
||||||
|
$table->index(['is_sticky', 'created_at']);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'down' => function (Builder $schema) {
|
||||||
|
$schema->table('discussions', function (Blueprint $table) {
|
||||||
|
$table->dropIndex(['is_sticky', 'created_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'up' => function (Builder $schema) {
|
||||||
|
$schema->table('discussions', function (Blueprint $table) {
|
||||||
|
$table->index(['is_sticky', 'last_posted_at']);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'down' => function (Builder $schema) {
|
||||||
|
$schema->table('discussions', function (Blueprint $table) {
|
||||||
|
$table->dropIndex(['is_sticky', 'last_posted_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
36
extensions/sticky/src/Event/DiscussionWasStickied.php
Normal file
36
extensions/sticky/src/Event/DiscussionWasStickied.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Event;
|
||||||
|
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class DiscussionWasStickied
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Discussion
|
||||||
|
*/
|
||||||
|
public $discussion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Discussion $discussion
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function __construct(Discussion $discussion, User $user)
|
||||||
|
{
|
||||||
|
$this->discussion = $discussion;
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
}
|
36
extensions/sticky/src/Event/DiscussionWasUnstickied.php
Normal file
36
extensions/sticky/src/Event/DiscussionWasUnstickied.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Event;
|
||||||
|
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class DiscussionWasUnstickied
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Discussion
|
||||||
|
*/
|
||||||
|
public $discussion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Discussion $discussion
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function __construct(Discussion $discussion, User $user)
|
||||||
|
{
|
||||||
|
$this->discussion = $discussion;
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
}
|
51
extensions/sticky/src/Listener/CreatePostWhenDiscussionIsStickied.php
Executable file
51
extensions/sticky/src/Listener/CreatePostWhenDiscussionIsStickied.php
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Listener;
|
||||||
|
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasStickied;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasUnstickied;
|
||||||
|
use Flarum\Sticky\Post\DiscussionStickiedPost;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class CreatePostWhenDiscussionIsStickied
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param DiscussionWasStickied $event
|
||||||
|
*/
|
||||||
|
public static function whenDiscussionWasStickied(DiscussionWasStickied $event)
|
||||||
|
{
|
||||||
|
static::stickyChanged($event->discussion, $event->user, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DiscussionWasUnstickied $event
|
||||||
|
*/
|
||||||
|
public static function whenDiscussionWasUnstickied(DiscussionWasUnstickied $event)
|
||||||
|
{
|
||||||
|
static::stickyChanged($event->discussion, $event->user, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Discussion $discussion
|
||||||
|
* @param User $user
|
||||||
|
* @param bool $isSticky
|
||||||
|
*/
|
||||||
|
protected static function stickyChanged(Discussion $discussion, User $user, $isSticky)
|
||||||
|
{
|
||||||
|
$post = DiscussionStickiedPost::reply(
|
||||||
|
$discussion->id,
|
||||||
|
$user->id,
|
||||||
|
$isSticky
|
||||||
|
);
|
||||||
|
|
||||||
|
$discussion->mergePost($post);
|
||||||
|
}
|
||||||
|
}
|
43
extensions/sticky/src/Listener/SaveStickyToDatabase.php
Executable file
43
extensions/sticky/src/Listener/SaveStickyToDatabase.php
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Listener;
|
||||||
|
|
||||||
|
use Flarum\Discussion\Event\Saving;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasStickied;
|
||||||
|
use Flarum\Sticky\Event\DiscussionWasUnstickied;
|
||||||
|
|
||||||
|
class SaveStickyToDatabase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Saving $event
|
||||||
|
*/
|
||||||
|
public function handle(Saving $event)
|
||||||
|
{
|
||||||
|
if (isset($event->data['attributes']['isSticky'])) {
|
||||||
|
$isSticky = (bool) $event->data['attributes']['isSticky'];
|
||||||
|
$discussion = $event->discussion;
|
||||||
|
$actor = $event->actor;
|
||||||
|
|
||||||
|
$actor->assertCan('sticky', $discussion);
|
||||||
|
|
||||||
|
if ((bool) $discussion->is_sticky === $isSticky) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$discussion->is_sticky = $isSticky;
|
||||||
|
|
||||||
|
$discussion->raise(
|
||||||
|
$discussion->is_sticky
|
||||||
|
? new DiscussionWasStickied($discussion, $actor)
|
||||||
|
: new DiscussionWasUnstickied($discussion, $actor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
extensions/sticky/src/PinStickiedDiscussionsToTop.php
Executable file
71
extensions/sticky/src/PinStickiedDiscussionsToTop.php
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky;
|
||||||
|
|
||||||
|
use Flarum\Filter\FilterState;
|
||||||
|
use Flarum\Query\QueryCriteria;
|
||||||
|
use Flarum\Tags\Query\TagFilterGambit;
|
||||||
|
|
||||||
|
class PinStickiedDiscussionsToTop
|
||||||
|
{
|
||||||
|
public function __invoke(FilterState $filterState, QueryCriteria $criteria)
|
||||||
|
{
|
||||||
|
if ($criteria->sortIsDefault) {
|
||||||
|
$query = $filterState->getQuery();
|
||||||
|
|
||||||
|
// If we are viewing a specific tag, then pin all stickied
|
||||||
|
// discussions to the top no matter what.
|
||||||
|
$filters = $filterState->getActiveFilters();
|
||||||
|
|
||||||
|
if ($count = count($filters)) {
|
||||||
|
if ($count === 1 && $filters[0] instanceof TagFilterGambit) {
|
||||||
|
if (! is_array($query->orders)) {
|
||||||
|
$query->orders = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if we are viewing "all discussions", only pin stickied
|
||||||
|
// discussions to the top if they are unread. To do this in a
|
||||||
|
// performant way we create another query which will select all
|
||||||
|
// stickied discussions, marry them into the main query, and then
|
||||||
|
// reorder the unread ones up to the top.
|
||||||
|
$sticky = clone $query;
|
||||||
|
$sticky->where('is_sticky', true);
|
||||||
|
$sticky->orders = null;
|
||||||
|
|
||||||
|
$query->union($sticky);
|
||||||
|
|
||||||
|
$read = $query->newQuery()
|
||||||
|
->selectRaw(1)
|
||||||
|
->from('discussion_user as sticky')
|
||||||
|
->whereColumn('sticky.discussion_id', 'id')
|
||||||
|
->where('sticky.user_id', '=', $filterState->getActor()->id)
|
||||||
|
->whereColumn('sticky.last_read_post_number', '>=', 'last_post_number');
|
||||||
|
|
||||||
|
// Add the bindings manually (rather than as the second
|
||||||
|
// argument in orderByRaw) for now due to a bug in Laravel which
|
||||||
|
// would add the bindings in the wrong order.
|
||||||
|
$query->orderByRaw('is_sticky and not exists ('.$read->toSql().') and last_posted_at > ? desc')
|
||||||
|
->addBinding(array_merge($read->getBindings(), [$filterState->getActor()->read_time ?: 0]), 'union');
|
||||||
|
|
||||||
|
$query->unionOrders = array_merge($query->unionOrders, $query->orders);
|
||||||
|
$query->unionLimit = $query->limit;
|
||||||
|
$query->unionOffset = $query->offset;
|
||||||
|
|
||||||
|
$query->limit = $sticky->limit = $query->offset + $query->limit;
|
||||||
|
$query->offset = $sticky->offset = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
extensions/sticky/src/Post/DiscussionStickiedPost.php
Executable file
79
extensions/sticky/src/Post/DiscussionStickiedPost.php
Executable file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Post;
|
||||||
|
|
||||||
|
use Flarum\Post\AbstractEventPost;
|
||||||
|
use Flarum\Post\MergeableInterface;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
|
||||||
|
class DiscussionStickiedPost extends AbstractEventPost implements MergeableInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $type = 'discussionStickied';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function saveAfter(Post $previous = null)
|
||||||
|
{
|
||||||
|
// If the previous post is another 'discussion stickied' post, and it's
|
||||||
|
// by the same user, then we can merge this post into it. If we find
|
||||||
|
// that we've in fact reverted the sticky status, delete it. Otherwise,
|
||||||
|
// update its content.
|
||||||
|
if ($previous instanceof static && $this->user_id === $previous->user_id) {
|
||||||
|
if ($previous->content['sticky'] != $this->content['sticky']) {
|
||||||
|
$previous->delete();
|
||||||
|
} else {
|
||||||
|
$previous->content = $this->content;
|
||||||
|
|
||||||
|
$previous->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance in reply to a discussion.
|
||||||
|
*
|
||||||
|
* @param int $discussionId
|
||||||
|
* @param int $userId
|
||||||
|
* @param bool $isSticky
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function reply($discussionId, $userId, $isSticky)
|
||||||
|
{
|
||||||
|
$post = new static;
|
||||||
|
|
||||||
|
$post->content = static::buildContent($isSticky);
|
||||||
|
$post->created_at = time();
|
||||||
|
$post->discussion_id = $discussionId;
|
||||||
|
$post->user_id = $userId;
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the content attribute.
|
||||||
|
*
|
||||||
|
* @param bool $isSticky Whether or not the discussion is stickied.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function buildContent($isSticky)
|
||||||
|
{
|
||||||
|
return ['sticky' => (bool) $isSticky];
|
||||||
|
}
|
||||||
|
}
|
44
extensions/sticky/src/Query/StickyFilterGambit.php
Normal file
44
extensions/sticky/src/Query/StickyFilterGambit.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Sticky\Query;
|
||||||
|
|
||||||
|
use Flarum\Filter\FilterInterface;
|
||||||
|
use Flarum\Filter\FilterState;
|
||||||
|
use Flarum\Search\AbstractRegexGambit;
|
||||||
|
use Flarum\Search\SearchState;
|
||||||
|
use Illuminate\Database\Query\Builder;
|
||||||
|
|
||||||
|
class StickyFilterGambit extends AbstractRegexGambit implements FilterInterface
|
||||||
|
{
|
||||||
|
protected function getGambitPattern()
|
||||||
|
{
|
||||||
|
return 'is:sticky';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function conditions(SearchState $search, array $matches, $negate)
|
||||||
|
{
|
||||||
|
$this->constrain($search->getQuery(), $negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilterKey(): string
|
||||||
|
{
|
||||||
|
return 'sticky';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(FilterState $filterState, string $filterValue, $negate)
|
||||||
|
{
|
||||||
|
$this->constrain($filterState->getQuery(), $negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function constrain(Builder $query, bool $negate)
|
||||||
|
{
|
||||||
|
$query->where('is_sticky', ! $negate);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user