Compare commits

...

416 Commits
1.2.1 ... 3

Author SHA1 Message Date
Guy Sartorelli 277f84392f
Merge branch '3.10' into 3 2023-08-29 10:28:27 +12:00
Steve Boyd ca593443d1
Merge pull request #374 from creative-commoners/pulls/3.10/tx-1692323162
ENH Update translations
2023-08-21 12:13:40 +12:00
Guy Sartorelli 67c42b363e
ENH Update translations 2023-08-18 13:46:02 +12:00
Guy Sartorelli 7e202debb7
Merge pull request #370 from silverstripe/dependabot/npm_and_yarn/word-wrap-1.2.5
Bump word-wrap from 1.2.3 to 1.2.5
2023-08-04 12:19:06 +12:00
dependabot[bot] b988465bf1
Bump word-wrap from 1.2.3 to 1.2.5
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-03 05:28:15 +00:00
Guy Sartorelli 4a9af98a70
Merge branch '3.10' into 3 2023-04-26 12:46:13 +12:00
Steve Boyd f5157aaf3c
Merge pull request #365 from creative-commoners/pulls/3.10/fix-constraints
MNT Revert erroneous dependency changes
2023-03-28 17:04:12 +13:00
Guy Sartorelli dde8ac9bac
MNT Revert erroneous dependency changes 2023-03-28 14:54:42 +13:00
Maxime Rainville dda2dcbfff
Merge pull request #364 from creative-commoners/pulls/3.10/no-deprecations
FIX Don't use deprecated API
2023-03-28 11:15:44 +13:00
Guy Sartorelli 7448c2050a
FIX Don't use deprecated API 2023-03-28 09:52:10 +13:00
Maxime Rainville 2fccdbe916
Merge pull request #363 from creative-commoners/pulls/3/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:10:18 +13:00
Steve Boyd 5729dc5c70 MNT Use gha-dispatch-ci 2023-03-21 12:23:30 +13:00
Guy Sartorelli 428b503a70
MNT Update development dependencies 2023-03-10 16:33:11 +13:00
Guy Sartorelli 1dc37ac0c1
MNT Update release dependencies 2023-03-10 16:33:07 +13:00
Guy Sartorelli 1ad761698b
MNT Update development dependencies 2023-03-10 12:21:29 +13:00
Guy Sartorelli e08e51d7f3
Merge pull request #362 from creative-commoners/pulls/3/tx-1678079758
ENH Update translations
2023-03-08 10:25:09 +13:00
Steve Boyd 0c7b2e987b ENH Update translations 2023-03-06 18:15:58 +13:00
Sabina Talipova 5d044bd6eb
Merge pull request #352 from creative-commoners/pulls/3/stop-using-depr
API Stop using deprecated API
2022-12-06 09:44:15 +13:00
Steve Boyd b771bb624a API Stop using deprecated API 2022-11-29 09:35:55 +13:00
Guy Sartorelli 9bfef9e931
Merge pull request #350 from creative-commoners/pulls/3/depr-messages
API Update deprecations
2022-11-21 09:52:02 +13:00
Guy Sartorelli 2b5bde3d8f
Merge pull request #351 from steffen-maass/allow-extension-override
Add a key when enabling the CommentsExtension in the config.
2022-11-20 17:44:39 +13:00
Steffen Maass 57a9617d5b Add a key when enabling the CommentsExtension.
This allows modules to unset it if necessary.
2022-11-19 16:42:56 -08:00
Steve Boyd c3ef4d6efc API Update deprecations 2022-11-16 12:13:50 +13:00
Steve Boyd a88bd6fff6 Merge branch '3.8' into 3 2022-08-02 18:47:23 +12:00
Steve Boyd 068f283833 Merge branch '3.7' into 3.8 2022-08-02 18:47:19 +12:00
Guy Sartorelli bffe690afd
Merge pull request #345 from creative-commoners/pulls/3.7/standardise-modules
MNT Standardise modules
2022-08-02 15:01:30 +12:00
Steve Boyd 6081aeba8f MNT Standardise modules 2022-08-01 16:21:22 +12:00
Guy Sartorelli 6974bfd8ba
Merge pull request #344 from creative-commoners/pulls/3/update-js
MNT Use up­date-js action
2022-07-29 17:06:35 +12:00
Steve Boyd b9341d6185 MNT Use update-js action 2022-07-29 13:07:50 +12:00
Steve Boyd a3f2f3cd48 Merge branch '3.8' into 3 2022-07-25 11:20:55 +12:00
Steve Boyd 05a3628471 Merge branch '3.7' into 3.8 2022-07-25 11:20:39 +12:00
Guy Sartorelli bf6e56bd10
Merge pull request #342 from creative-commoners/pulls/3.7/module-standards
MNT Use GitHub Actions CI
2022-07-15 11:27:29 +12:00
Steve Boyd ff6db7d480 MNT Use GitHub Actions CI 2022-07-14 12:06:51 +12:00
Will Rossiter d87dbc8367
fix: field formatting to correct include link 2022-06-13 11:53:44 +12:00
Steve Boyd 6782048463
Merge pull request #337 from silverstripe/pulls/3/update-js-1648772023
DEP Update JS dependencies
2022-05-31 11:02:37 +12:00
dependabot[bot] a3479443df
Merge pull request #339 from silverstripe/dependabot/npm_and_yarn/async-2.6.4 2022-05-03 09:51:13 +00:00
dependabot[bot] 9ef77e393c
Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-28 19:49:18 +00:00
Guy Sartorelli 3dc22e9c39
Merge pull request #338 from creative-commoners/pulls/3/php81
ENH PHP 8.1 compatibility
2022-04-26 17:58:35 +12:00
Steve Boyd e7f9e60352 ENH PHP 8.1 compatibility 2022-04-13 10:22:29 +12:00
github-actions efe5b1ef6f DEP Update JS dependencies 2022-04-01 00:13:43 +00:00
Maxime Rainville 3d6e44aaa1
Merge pull request #336 from creative-commoners/pulls/3/update-js-deps
MNT Add update JS deps workflow
2022-03-23 15:45:13 +13:00
Steve Boyd 1d6eae193c MNT Add update JS deps workflow 2022-03-22 12:06:51 +13:00
Daniel Hensby 7284a920ed
Merge pull request #335 from creative-commoners/pulls/3/php74
DEP Set PHP 7.4 as the minimum version
2022-02-10 12:05:31 +00:00
Steve Boyd 536d965d9c DEP Set PHP 7.4 as the minimum version 2022-02-10 15:55:36 +13:00
Steve Boyd 1aa560ff74 Merge branch '3.7' into 3 2021-12-22 10:25:58 +13:00
Steve Boyd 421e08cba5
Merge pull request #334 from creative-commoners/pulls/3.7/cherry-pick
MNT Fix unit test
2021-11-29 17:25:45 +13:00
Steve Boyd 91d2db1020 MNT Fix unit test 2021-11-29 17:24:29 +13:00
Steve Boyd 9258ce6fa4 Merge branch '3.7' into 3 2021-11-25 13:16:34 +13:00
Maxime Rainville 8a3b569653
Merge pull request #333 from creative-commoners/pulls/3.7/js
DEP Update JS dependencies
2021-11-24 17:26:28 +13:00
Steve Boyd aa81f8cdd2 DEP Update JS dependencies 2021-11-23 10:45:13 +13:00
Steve Boyd 6895ac4f9d
Merge pull request #331 from creative-commoners/pulls/3/eslint
MNT Use caret for eslint
2021-11-22 13:35:29 +13:00
Steve Boyd a3c7943fdd
Merge pull request #330 from creative-commoners/pulls/3/fix-test
MNT Fix unit test
2021-11-22 11:56:08 +13:00
Steve Boyd 43e221daa8 MNT Use caret for eslint 2021-11-19 12:29:17 +13:00
Steve Boyd 6d9a808cd4 MNT Fix unit test 2021-11-19 10:55:22 +13:00
Maxime Rainville 7eb8ebedf4
Merge pull request #329 from creative-commoners/pulls/3/sapphire-test-nine
API phpunit 9 support
2021-11-01 17:52:24 +13:00
Steve Boyd 305ae6b4b8 API phpunit 9 support 2021-10-27 18:03:55 +13:00
Loz Calver 066b28bb3d
Fix broken link in README.md 2021-10-14 16:22:35 +01:00
dependabot[bot] 5cee54faca
Merge pull request #324 from silverstripe/dependabot/npm_and_yarn/hosted-git-info-2.8.9 2021-08-05 06:25:32 +00:00
dependabot[bot] 4cfecdfcd6
Merge pull request #325 from silverstripe/dependabot/npm_and_yarn/ws-6.2.2 2021-06-06 15:57:51 +00:00
dependabot[bot] 3a4130302d
Bump ws from 6.2.1 to 6.2.2
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-05 15:06:53 +00:00
dependabot[bot] a4eeec4bba
Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-10 06:32:37 +00:00
Steve Boyd 545df1d54f Merge branch '3.4' into 3 2021-03-21 10:31:41 +13:00
Steve Boyd 9c3c1e8120 Fix merge conflict 2021-03-21 10:31:02 +13:00
Steve Boyd 38495c69f0 Merge branch '3.4' into 3 2021-03-21 10:28:31 +13:00
Steve Boyd e6971b82c6 Merge branch '3.3' into 3.4 2021-03-21 10:27:53 +13:00
Steve Boyd 362cb05029 Merge branch '3.4' into 3 2021-03-20 10:03:18 +13:00
Steve Boyd 5bda1a7076 Merge branch '3.3' into 3.4 2021-03-20 09:58:21 +13:00
Garion Herman 175664d5a8
Merge pull request #320 from creative-commoners/pulls/3.4/jquery-yarn
ENH Use yarn and webpack to upgrade jquery
2021-03-04 17:29:12 +13:00
Steve Boyd 3b37fdb1ae ENH Use yarn and webpack to upgrade jquery 2021-03-04 17:05:29 +13:00
Steve Boyd fb15ba7d43
Update build status badge 2021-01-21 16:32:17 +13:00
Garion Herman 06b243b1e4
Merge pull request #316 from creative-commoners/pulls/3.3/shared-config
MNT Use shared travis config, use sminnee/phpunit
2021-01-18 10:30:53 +13:00
Steve Boyd 668f90d2f9 Merge branch '3.4' into 3 2021-01-02 19:42:12 +13:00
Maxime Rainville 867d0e8281
Merge pull request #318 from creative-commoners/pulls/3.4/travis-shared
MNT Travis shared config, use sminnee/phpunit
2020-12-21 14:46:25 +13:00
Steve Boyd c065d0c5de MNT Travis shared config, use sminnee/phpunit 2020-12-01 20:34:24 +13:00
Steve Boyd 739b211afb ENH Update translations 2020-11-18 12:56:01 +13:00
Garion Herman d12a71ddff
Merge pull request #311 from silverstripe/dependabot/npm_and_yarn/node-sass-4.14.1
Bump node-sass from 4.13.0 to 4.14.1
2020-11-17 10:42:35 +13:00
Steve Boyd c834e19631 MNT Use shared travis config, use sminnee/phpunit 2020-11-16 15:24:51 +13:00
dependabot[bot] a8d1cf1355
Bump node-sass from 4.13.0 to 4.14.1
Bumps [node-sass](https://github.com/sass/node-sass) from 4.13.0 to 4.14.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.13.0...v4.14.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 10:37:02 +00:00
Garion Herman f5f6e8f08f
Merge pull request #309 from silverstripe/dependabot/npm_and_yarn/elliptic-6.5.3
Bump elliptic from 6.5.1 to 6.5.3
2020-08-19 16:33:48 +12:00
dependabot[bot] 8bc492bb77
Bump elliptic from 6.5.1 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.1 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.1...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 14:08:55 +00:00
dependabot[bot] 68ec979987
Merge pull request #308 from silverstripe/dependabot/npm_and_yarn/npm-6.14.7 2020-07-22 10:37:41 +00:00
dependabot[bot] f91b13ad96
Bump npm from 6.13.4 to 6.14.7
Bumps [npm](https://github.com/npm/cli) from 6.13.4 to 6.14.7.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/v6.14.7/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.13.4...v6.14.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-21 20:23:55 +00:00
dependabot[bot] 6972da84a6
Merge pull request #306 from silverstripe/dependabot/npm_and_yarn/npm-registry-fetch-4.0.5 2020-07-21 20:22:31 +00:00
dependabot[bot] 99b2ed4dfc
Merge pull request #307 from silverstripe/dependabot/npm_and_yarn/lodash-4.17.19 2020-07-21 20:22:23 +00:00
dependabot[bot] 1e9d8fcd1b
Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-16 22:42:39 +00:00
dependabot[bot] bcbc58f46e
Bump npm-registry-fetch from 4.0.2 to 4.0.5
Bumps [npm-registry-fetch](https://github.com/npm/registry-fetch) from 4.0.2 to 4.0.5.
- [Release notes](https://github.com/npm/registry-fetch/releases)
- [Changelog](https://github.com/npm/npm-registry-fetch/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/registry-fetch/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-08 00:40:27 +00:00
Steve Boyd e88c7d5154
Merge pull request #301 from torleif/patch-1
FIX Broken action checkboxes
2020-07-03 11:23:50 +12:00
Maxime Rainville edfb9d7b74 Merge branch '3.3' into 3 2020-06-12 14:15:04 +12:00
Maxime Rainville 932ef5d284 Merge branch '3.2' into 3.3 2020-06-12 14:14:32 +12:00
torleif 82c817c452
Fixes #300 2020-04-20 09:40:57 +12:00
Robbie Averill aaa9328a72
Merge pull request #299 from silverstripe/dependabot/npm_and_yarn/npm-6.13.4
Bump npm from 6.13.0 to 6.13.4
2019-12-13 14:05:12 -08:00
dependabot[bot] e2db114158
Bump npm from 6.13.0 to 6.13.4
Bumps [npm](https://github.com/npm/cli) from 6.13.0 to 6.13.4.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.13.0...v6.13.4)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-13 20:02:32 +00:00
Garion Herman cfdffb394a Merge branch '3.3' into 3 2019-11-18 12:13:59 +13:00
Garion Herman d0b6181b48 Update config for 3.3 / CMS 4.5 branches 2019-11-18 12:12:58 +13:00
Andre Kiste ce450103ac
Merge pull request #298 from creative-commoners/pulls/3/upgrade-dependnecies
Restore empty comments.css file
2019-11-12 12:26:55 +13:00
Maxime Rainville 05c5cb64a7 Restore empty comments.css file 2019-11-12 11:53:35 +13:00
Andre Kiste 408845b01a
Merge pull request #297 from creative-commoners/pulls/3/upgrade-dependnecies
Upgrade dependencies and build settings
2019-11-12 11:42:30 +13:00
Maxime Rainville 85374a1f76 Upgrade dependencies and build settings 2019-11-11 12:18:54 +13:00
Dylan Wagstaff 07b24073dd
Merge pull request #295 from silverstripe/dependabot/npm_and_yarn/mixin-deep-1.3.2
Bump mixin-deep from 1.3.1 to 1.3.2
2019-10-04 10:55:56 +13:00
dependabot[bot] 250e522887
Bump mixin-deep from 1.3.1 to 1.3.2
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-03 21:09:03 +00:00
Dylan Wagstaff 9d9ab35cc6
Merge pull request #294 from AdamSawoscianik/patch-1
Update Installing.md
2019-10-04 10:07:48 +13:00
AdamSawoscianik d5b9a2818c
Update Installing.md
Adding information about $CommentsForm tag to help people with this module.
2019-10-03 10:32:22 +02:00
Guy Marriott e21a09234a
Update translations 2019-08-20 16:37:58 +12:00
Jason Irish fbfead9c9e BUGFIX CommentAdmin implements PermissionProvider
fixes #288
2019-08-15 09:50:36 +12:00
Will Rossiter b3fe6d82b4 Add legacy YAML for upgrading 2019-08-15 09:50:27 +12:00
Heath Dunlop 617501efed comments extension filters on Parent Class
This allows both pages and DataObjects to have comments without the ParentID clashing and showing comments on a page where the ParentID is the Same as a DataObjects or even two DataObjects having the sameID
2019-08-15 09:50:19 +12:00
Robbie Averill f4c82f238e
Merge pull request #292 from jsirish/bugfix/adminPermissionProvider
BUGFIX CommentAdmin implements PermissionProvider
2019-07-15 22:03:28 +01:00
Jason Irish c0f0c99e56 BUGFIX CommentAdmin implements PermissionProvider
fixes #288
2019-07-15 12:02:50 -05:00
Dylan Wagstaff f8eeeee4b8
Merge pull request #290 from GOVTNZ/pulls/legacy
Add legacy YAML for upgrading
2019-07-03 12:47:47 +12:00
Will Rossiter cad9369f7b
Add legacy YAML for upgrading 2019-07-03 11:40:42 +12:00
Robbie Averill c16ca56408
Merge pull request #289 from creative-commoners/pulls/3.2/focus-comment-submission-message
NEW Add tabindex="-1" to comment submission message for a11y support
2019-06-28 16:15:27 +12:00
Garion Herman 67167c0809 NEW Add tabindex="-1" to comment submission message for a11y support 2019-06-28 15:56:09 +12:00
Robbie Averill 8397bb5792
Merge pull request #287 from creative-commoners/pulls/master/update-vuln-dependencies
Update @silverstripe/webpack-config to patch vulnerable deps, add NVM config
2019-06-14 15:04:14 +12:00
Garion Herman 2d0d949fc3 Update @silverstripe/webpack-config to patch vulnerable deps 2019-06-14 14:47:09 +12:00
Garion Herman 14675b53bf Add NVM config 2019-06-14 13:59:23 +12:00
Robbie Averill 9c4ec173fd
Merge pull request #285 from dunatron/patch-2
comments extension filters on Parent Class
2019-05-16 15:33:09 +12:00
Heath Dunlop 82845626bc
comments extension filters on Parent Class
This allows both pages and DataObjects to have comments without the ParentID clashing and showing comments on a page where the ParentID is the Same as a DataObjects or even two DataObjects having the sameID
2019-05-16 13:46:28 +12:00
Robbie Averill 780ea22b93 Merge branch '3.2' 2019-05-10 09:35:00 +12:00
Robbie Averill f65f5b5697 FIX Remove reliance on translations in fieldLabels test, run textcollector, remove deprecated code from CommentsTest 2019-05-10 09:33:18 +12:00
Guy Marriott 74a317ab46
Update translations 2019-05-09 15:44:57 +12:00
Robbie Averill 161f5f88bd Merge branch '3.2' 2019-05-08 11:28:18 +12:00
Robbie Averill 67faa822f7 Update dependencies 2019-05-08 11:26:56 +12:00
Guy Marriott aec3e2ba5b
FIX "Allow Comments" label now reads "Allow comments" - should not have been capitalised (#283)
FIX "Allow Comments" label now reads "Allow comments" - should not have been capitalised
2019-05-02 13:12:10 +12:00
Robbie Averill 89204c0c1c FIX "Allow Comments" label now reads "Allow comments" - should not have been capitalised 2019-05-02 11:31:32 +12:00
Robbie Averill d8602d0e02 Merge branch '3.2' 2019-04-15 15:34:34 +12:00
Robbie Averill 4d19e6c43e Merge branch '3.1' into 3.2 2019-04-15 15:33:53 +12:00
Robbie Averill 303bdef1c8 Remove obsolete branch alias and add PHP 7.3 to Travis builds 2019-04-15 15:33:44 +12:00
Guy Marriott 1358594070
Merge pull request #275 from creative-commoners/pulls/3.1/update-jquery-validator
Update jQuery.validation to 1.19.0
2019-04-05 09:17:06 +13:00
Dylan Wagstaff 6fcc040485 Update jQuery.validation to 1.19.0
jQuery 3.3.1 is still the latest release
2019-03-25 09:56:58 +13:00
Dylan Wagstaff 8b178b63ac
Merge pull request #280 from creative-commoners/pulls/master/improve-compat-with-bulk-editing-tools
Adjust the output of CommentHandler to use HTTPBulkToolsResponse
2019-03-24 13:34:21 +13:00
Garion Herman 1e4378d212 Tighten version constraint for Bulk Editing Tools dependency 2019-03-22 15:53:12 +13:00
Robbie Averill f0998faafb
Allow translation of 'Changes applied' message on CommentHandler
Co-Authored-By: Cheddam <cheddam@me.com>
2019-03-22 15:43:19 +13:00
Garion Herman 74afde6d9b Adjust the output of CommentHandler to use HTTPBulkToolsResponse
GridFieldBulkEditingTools has introduced a common output schema
in the form of HTTPBulkToolsResponse. This commit adopts that API.
2019-03-22 15:00:19 +13:00
Garion Herman 9ff1db403e
Merge pull request #277 from creative-commoners/pulls/4.0/infix-spam-flicks
NEW relocate spam and approve admin actions
2019-03-22 11:10:29 +13:00
Robbie Averill ce1e934d57
Merge pull request #278 from SpeksForks/doc-link
Fix documentation link
2019-02-19 05:09:33 +07:00
Peter Thaleikis 005f58f1ee
Fix documentation link 2019-02-19 02:32:09 +07:00
Robbie Averill 647c634dc4
Update .travis.yml
Co-Authored-By: NightJar <NightJar@users.noreply.github.com>
2019-02-11 09:22:02 +13:00
Guy Marriott 7d60fc2fe2
Update src/Admin/CommentsGridFieldSpamAction.php
Co-Authored-By: NightJar <NightJar@users.noreply.github.com>
2019-02-11 09:19:31 +13:00
Guy Marriott 18fbdfc94b
Update src/Admin/CommentsGridFieldApproveAction.php
Co-Authored-By: NightJar <NightJar@users.noreply.github.com>
2019-02-11 09:19:18 +13:00
Robbie Averill c4cfc44643 Update Scrutinizer configuration to use new build engine 2019-02-10 07:01:57 +03:00
Dylan Wagstaff b6a2c608ac NEW relocate spam and approve admin actions
Previous versions had individual spam and approve buttons for a comment
in the admin area on the GridField row. However with the upgrade to
SilverStripe 4, and particularly 4.2, these were having layout issues
with the new GridField Action Menu component that groups actions
together.

The solution here is to put them into aforementioned gridfield action
menu component, with the other actions for that row. However this
requires two separate grid field components (one each for the two
comment actions) - previously these were a single component that output
two buttons instead of one each. This also reduces coupling, which is
nice :)

The previous class is still maintained for backwards compatibilty, but
is deprecated.
2019-02-08 15:19:45 +13:00
Robbie Averill d504d4a847 Merge branch '3.1' 2019-01-11 09:13:31 +01:00
Guy Marriott f5c7024aaf
Merge pull request #276 from creative-commoners/pulls/3.1/deprotocol-my-site
Validate protocol-less URLs
2019-01-11 15:18:27 +13:00
Dylan Wagstaff 3812057b00 Add ability for commenters to enter a URL without a protocol
When posting a comment on the a page with this module applied, there is
an optional input for the commenter to give their URL,presumably their
website. However this input currently validates (via JavaScript) to
allow URLs only iff they have a protocol.

Common use cases when someone is asked for their website in my
experience is to then receive a URL without a protocol, confounded in
that most web browsers will accept this form and automatically add the
http protocol, where a webserver may then redirect to https by default.
This means that all the magic happens behind the scenes and most folks
don't particularly care to think about protocols when entering web
addresses.

Yest this input will only validate true and allow comment submission if
they do.

So now it will allow a protocol-less entry into the URL box.
2019-01-11 15:01:34 +13:00
Guy Marriott 20c6a50f12
Merge pull request #273 from creative-commoners/pulls/3.1/name-that-member
FIX show name of members in summary tables
2019-01-11 14:56:20 +13:00
Dylan Wagstaff 0535d29f8d
Merge pull request #274 from creative-commoners/pulls/3.1/i-de-test-those-labels
FIX Correcting inconsistent capitalisation of label and cleaning up tests
2019-01-11 14:49:02 +13:00
Guy Marriott 7691237fe5
FIX Correcting inconsistent capitalisation of label 2019-01-11 14:17:36 +13:00
Guy Marriott a57c83c121
Removing superflous i18n test and updating label test to only test labels specifically added 2019-01-11 12:22:31 +13:00
Dylan Wagstaff dc1f8622e0 FIX show name of members in summary tables
Anonymous comments (posted by the public at large) must have a name
and an email address associated with them. On the other hand, a logged
in user will have the `Member` record details used for this information,
via the Author relationship.

However the summary fields do not allow for this, and only reference
Name and Email on the Comment model directly, so any comment posted by a
logged in member has no data for name and email displayed in the various
GridFields in the CMS for administering comments (either per page, or in
the global ModelAdmin).

To recitfy this we can change the summary fields to use getter methods
that will return the Comment model info, or fall back to the Author
associated Member record if Name and Email are unset on the Comment.
2019-01-11 11:48:51 +13:00
Dylan Wagstaff 4ee9720081
Merge pull request #269 from creative-commoners/pulls/3.1/moderation-labels
FIX Capitalise comment moderation action labels and update i18n syntax in templates
2018-11-28 14:36:30 +13:00
Dylan Wagstaff d7761290f8
Merge pull request #268 from creative-commoners/pulls/3.1/fix-rss-comments-on-page
FIX RSS feed for comments now render correctly
2018-11-28 14:35:48 +13:00
Robbie Averill e101b74778 FIX Capitalise comment moderation action labels and update i18n syntax in templates 2018-11-27 13:27:19 +01:00
Robbie Averill 8c08f43376 FIX RSS feed for comments now render correctly
The comment DataList filter applies ParentClass = $className - when this
is the base class, it fails to recognise comments for a BlogPost. This
change uses the late class name instead.
2018-11-27 13:19:55 +01:00
Dylan Wagstaff f74d91041b
Merge pull request #267 from creative-commoners/pulls/3.1/remove-json-methods
FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4
2018-10-29 11:05:43 +13:00
Robbie Averill d38e7c5b67 FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4 2018-10-28 21:31:19 +00:00
Robbie Averill 97461f8f35 Merge branch '3.1' 2018-10-05 11:51:41 +02:00
Guy Marriott def91bb6d1
Merge pull request #265 from creative-commoners/pulls/3.1/minor-tidyups
Minor tidy ups to code formatting
2018-09-25 11:07:07 +12:00
Robbie Averill ec60328a0c Use short brackets, injection where possible and type hint injector created objects 2018-09-24 18:20:38 +02:00
Robbie Averill c0ca79090b Use injection where possible and remove redundant code lines 2018-09-24 18:11:26 +02:00
Robbie Averill 9a57c3802c Remove redundant phpcs rules, reduce some line lengths and use injector to create checkbox fields 2018-09-24 18:09:25 +02:00
Robbie Averill 9f9dc67950
Merge pull request #264 from caffeineinc/bugfix-3.1/xss-session-cookies
Prevent storing formdata to cookies.
2018-09-24 18:07:07 +02:00
Robbie Averill 94950ee79c Tidy up phpcs violations, use short array syntax, optimise class imports 2018-09-24 17:57:31 +02:00
Simon Gow 747d4f4402 Prevent storing formdata to cookies.
- XSS an be stored in a cookie and potentially abused in other ways, so
to prevent this we need to use session instead. This requires the user
to have a session with silverstripe, but this is better than saving
potentially malicious content in cookies. (Also some cookies have
limited length).

@see https://github.com/silverstripe/silverstripe-comments/issues/263
2018-09-20 14:53:35 +12:00
Robbie Averill cf86b2bb21
Merge pull request #262 from lukereative/pulls/3.1/fix-admin-height
FIX Comment admin form should fill height
2018-08-27 14:59:30 +12:00
Luke Edwards 75d63209aa FIX Comment admin form should fill height 2018-08-27 14:36:44 +12:00
Robbie Averill 04a5257e40 Merge branch '3.1' 2018-07-26 14:56:38 +12:00
Robbie Averill 19619b083e
Merge pull request #261 from micmania1/bugfix/canpostcomment-not-passed-to-include
BUGFIX canPostComment was being called out of scope
2018-07-03 11:43:34 +12:00
micmania1 c0a01dbc91 BUGFIX created way of knowing whether user has permission to post 2018-07-03 11:00:40 +12:00
Robbie Averill 6d7b14367a
Merge pull request #259 from quantum-dragons/bugfix/reply-form-js-fix
Added JS fix for reply forms
2018-06-25 14:08:08 +12:00
pjayme 77f47af5cb Added JS fix for reply forms - fixes issue where the submit button breaks due to incorrect use of the jQuery validate function 2018-06-25 13:25:08 +12:00
Dylan Wagstaff a1a42009cc
Merge pull request #258 from creative-commoners/pulls/3.1/travis-recipes
Add various recipe versions to Travis build matrix
2018-06-22 20:10:14 +12:00
Robbie Averill c7235e1c5d FIX Comments GridField tests now use their own test stubs 2018-06-20 16:53:14 +12:00
Robbie Averill 4e23771d79 Add Team stub class to extra_dataobjects 2018-06-20 16:48:26 +12:00
Robbie Averill 52b8ea9104 Add various recipe versions to Travis build matrix 2018-06-20 16:39:46 +12:00
Robbie Averill 8bd79eac1f Merge branch '3.1' 2018-06-20 16:28:40 +12:00
Robbie Averill f357ca6b7f
Merge pull request #257 from creative-commoners/pulls/3.1/reply-bug
FIX Bug with requiring login when posting a comment, pass correct controller in
2018-06-20 14:28:22 +12:00
Robbie Averill 886c5be21a FIX Bug with requiring login when posting a comment, pass correct controller in 2018-06-20 14:20:50 +12:00
Robbie Averill 9dab444f33
Merge pull request #255 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-18 10:49:55 +12:00
Dylan Wagstaff 2c00421cd2
Merge pull request #254 from creative-commoners/pulls/3.1/akismet-test-error
FIX Mock akismet spam protector if installed, fixes broken integration tests
2018-06-18 09:59:05 +12:00
Robbie Averill 32ec3bde50
FIX Add getDate method to return created date for comments, tidy up translations
RSS feeds are looking for $Date, so this method maps to the created field in these cases
2018-06-15 16:40:44 +01:00
Dylan Wagstaff be71d19d9e Add supported module badge to readme 2018-06-15 17:32:42 +12:00
Robbie Averill 788cb6e6d1 FIX Mock akismet spam protector if installed, fixes broken integration tests 2018-06-15 16:50:14 +12:00
Robbie Averill eca3ac0e94
FIX Allow tests to handle extra field labels being added in global state 2018-06-13 09:59:23 +12:00
Robbie Averill 01cc198a2d Merge branch '3.1' 2018-06-11 12:15:31 +12:00
Robbie Averill 06acad661f Remove obsolete branch alias 2018-06-11 12:15:09 +12:00
Robbie Averill 42a15f7241
Merge pull request #253 from creative-commoners/pulls/3.1/fix-tests
Fix Tests
2018-05-30 14:36:27 +12:00
Guy c22daa2ee0 FIX Removing ID from match in tests 2018-05-30 12:25:49 +12:00
Robbie Averill 614b525c04
FIX Loosen assertions in case extension provide extra columns in global state 2018-05-29 18:56:32 +12:00
Dylan Wagstaff 02db1cc86e [SS-2018-015] Update jQuery version, remove entwine from frontend use
jQuery version was extremely old, and was probably stuck at that as a way
of enabling the frivilous use of entwine on the front end for somewhat
trivial ajax submisions. A mild refactor has taken place to leverage newer
jQuery features, and remove outdated dependencies.

Also accompanying this commit are alterations to the markup to make it
more semantically correct (probably not entirely though), and help with
testing the JS functionality of reply forms (when enabled).
2018-05-29 11:40:18 +12:00
Daniel Hensby aa46291adf
Merge pull request #249 from Dennisprins93/master 2018-05-11 14:14:58 +01:00
Dennisprins93 386f8602f0
Omit http because of potential https conflicts 2018-05-11 14:11:22 +02:00
Robbie Averill eb0415a938
Merge pull request #245 from creative-commoners/pulls/2.0/reallow-commentform-alterations
FIX reintroduce extension hook for comment form rendering
2018-04-04 16:36:51 +12:00
Raissa North a886f68c58 FIX reintroduce extension hook for comment form rendering
In CWP 1.x there was an extension hook to allow alterations to the commenting form.
Since the upgrade of this module for CWP 2.x this was no longer there, meaning functionality
that relied upon it no longer worked. This commit reintroduces this functionality to keep
other modules that apply extensions to that hook, happy.
2018-04-04 15:45:45 +12:00
Dylan 276ddb0c86 Update translations 2018-04-04 10:17:01 +12:00
Dylan Wagstaff cc46ccf89e
Merge pull request #244 from creative-commoners/pulls/3.1/revert-and-deprecate
API Reintroduce abstract handler (previously removed in 192ddbb) and deprecate for future removal
2018-04-04 08:34:08 +12:00
Robbie Averill 3e0cae0cc9 API Reintroduce abstract handler (previously removed in 192ddbb) and deprecate for future removal 2018-04-03 12:22:15 +12:00
Robbie Averill 4385299f1f
Merge pull request #240 from sasky/master
Tiny bug fixes to work with SS4.
2018-03-14 13:18:55 +13:00
Cameron Grant abb45aa733 Setting the http error to come from the request handler, and the session coming form the Request objects. Just small bugs fix's so the comment form works with ss4. 2018-03-14 11:18:30 +13:00
Robbie Averill 532b49c537
Merge pull request #235 from creative-commoners/pulls/3.0/run-test-independent-from-template
Use FunctionalTest instead of SapphireTest to disable themes.
2018-03-09 17:02:47 +13:00
Raissa North 8d5edc7821 Use FunctionalTest instead of SapphireTest to disable themes. 2018-03-09 16:45:38 +13:00
Robbie Averill 90c42ff027
Merge pull request #232 from creative-commoners/pulls/3.0/bulk-editing-compat
API Use concrete Handler implementations for Spam and Approve bulk editing
2018-02-27 09:29:26 +13:00
Daniel Hensby 909066c0a8
Move comment handles to shared parent class 2018-02-26 11:40:49 +00:00
Robbie Averill 192ddbb4b5 API Use concrete Handler implementations for Spam and Approve bulk editing 2018-02-26 23:24:34 +13:00
Daniel Hensby b90ec7715e
Merge pull request #228 from benwrighton/master 2018-02-02 10:45:48 +00:00
Unknown ae59e82021 Improve A11y of comment reply.
Currently this is a link. Users will expect this link to take them to another part of the page or another page altogether. 

There is no indication that it opens or closes an associated form on the same page, nor does it indicate the current state of the “reply to” form.

So, instead of a link, use a button. Add to that button an aria-controls attribute that references the id of the associated form’s container, and an aria-expanded attribute that indicates (true or false) the state of the associated form.
2018-02-01 16:27:30 +13:00
Robbie Averill 17714f221a
Merge pull request #227 from creative-commoners/pulls/3.0/tx-me-js
FIX: update javascript requirements so user JS doesn't error
2018-01-29 16:06:27 +13:00
Dylan Wagstaff a25668eba0 Remove SS 2.x upgrade code that runs every build 2018-01-29 15:26:16 +13:00
Dylan Wagstaff 7b38707fde FIX: update javascript requirements so user JS doesn't error 2018-01-29 13:04:34 +13:00
Robbie Averill 4bf0a83ad3 FIX Update CommentAdmin test to create a mock session and not assert missing translation 2017-12-19 09:32:56 +13:00
Robbie Averill b02928e02a Merge branch '2' 2017-12-19 09:14:47 +13:00
Robbie Averill 4136bd7a63 Update branch alias for 2.x-dev 2017-12-18 18:18:32 +13:00
Robbie Averill b68ff56624 Merge branch '2.0' into 2.1 2017-12-18 18:17:06 +13:00
Robbie Averill 4e2965e86b Remove PHP 5.3 from Travis builds 2017-12-18 18:16:58 +13:00
Robbie Averill 85c9981962 Remove Transifex configuration. Commit directly to lang files for SS3. 2017-12-18 18:14:53 +13:00
Robbie Averill 82c4a1b9be
DOCS Update readme badges and add version support note 2017-12-18 14:11:51 +13:00
Dylan Wagstaff 3501ef7490
Merge pull request #224 from creative-commoners/pulls/3.0/fix-comments-admin
FIX Duplicated tabset, invalid Pjax request, update translations and Spam/Approve buttons
2017-12-15 10:09:32 +13:00
Robbie Averill 75d7de9aa6 FIX Add extra DataObject stub to CommentsGridFieldActionTest and update expectations 2017-12-14 17:57:03 +13:00
Robbie Averill 216ad8eb54 FIX Duplicated tabset, invalid Pjax request, update translations and Spam/Approve buttons 2017-12-14 16:08:43 +13:00
Daniel Hensby 118de6b595
Merge pull request #222 from creative-commoners/pulls/2.0/vendorise
NEW Convert to vendor module, add table_name to test stubs and add PHP 7.2 to travis
2017-12-13 12:07:34 +00:00
Robbie Averill 3f6f651eea FIX Replace French translation in CommentAdminTest now that it's available 2017-12-12 17:12:10 +13:00
Robbie Averill c6f8973342 NEW Convert to vendor module, add table_name to test stubs and add PHP 7.2 to travis 2017-12-12 17:03:04 +13:00
Dylan Wagstaff 8919f6da2a Merge pull request #220 from sheadawson/patch-1
FIX SS4: use SilverStripe\CMS\Model\SiteTree in Comment.php
2017-10-26 14:16:05 +13:00
Shea Dawson b1e9722320 use SilverStripe\CMS\Model\SiteTree; 2017-10-26 13:54:49 +13:00
Will Rossiter 8c43e097cd Merge pull request #216 from fullscreeninteractive/pulls/travis-fix
Travis, 4.0 API fixes
2017-10-10 09:49:13 +13:00
Robbie Averill 7700d88d4a FIX Remove base path assertions from tests which behave differently using phpdbg 2017-10-10 09:26:17 +13:00
Robbie Averill fa61608c7d FIX Merge config in tests, add phpcs and add HTML purifier to Travis builds 2017-10-09 17:26:07 +13:00
Will Rossiter f294ab26d6 Update tests and lang’s for 4.0 2017-09-18 14:16:24 +12:00
Will Rossiter 9366ef603e Split comment form out to its’ own class 2017-09-16 09:06:03 +12:00
Will Rossiter d9333b3160 Test fixes 2017-09-14 11:12:07 +12:00
Will Rossiter 7fff17b317 Correct extra dataobjects array name 2017-09-14 10:28:29 +12:00
Will Rossiter 73a981456f Use trusty 2017-09-14 10:09:21 +12:00
Will Rossiter 2fc7e7a004 Add phpunit, lint fixes and removed compass for webpack 2017-09-14 10:01:09 +12:00
Will Rossiter 0c964d1ed8 Composer lint 2017-09-14 09:41:11 +12:00
Will Rossiter 82c139e30a Travis fix 2017-09-13 09:12:09 +12:00
Will Rossiter 18761879cf Remove old email 2017-09-13 08:52:41 +12:00
Will Rossiter 4e2d22e16d Merge pull request #214 from fullscreeninteractive/wilr-patch-1
If user cannot view record then prevent comment
2017-09-13 08:36:18 +12:00
Franco Springveldt 8e79a1368e Update translations 2017-08-28 16:35:55 +12:00
Will Rossiter 85e8384151 If user cannot view record then prevent comment 2017-07-28 17:12:01 +12:00
Daniel Hensby fb3cc1b861 Add .codecov.yml file 2017-07-19 16:04:34 +01:00
Robbie Averill 4df49c8db7 Merge pull request #201 from rasstislav/patch-1
Added missing translation to Comments TabSet Title
2017-07-06 10:31:38 +12:00
Daniel Hensby 06439dde1f Merge pull request #209 from andrewandante/FIX/update_session
update thirdpartydir and session non-statics
2017-07-05 11:50:02 +01:00
Daniel Hensby d74bc59b98 Merge pull request #210 from dhensby/pulls/remove-travis-5.5
Remove PHP 5.5 build from travis
2017-07-05 11:49:34 +01:00
Daniel Hensby f276983611
Remove PHP 5.5 build from travis 2017-07-05 11:02:59 +01:00
Andrew Aitken-Fincham e7c358f730 update thirdpartydir and session non-statics 2017-07-04 15:53:38 +01:00
Daniel Hensby 8383ff1f81 Stop codecov comments 2017-06-18 01:04:58 +01:00
Daniel Hensby bd5b007394 Merge pull request #206 from creative-commoners/pulls/2.1/remove-broken-i18n-tests
FIX Remove providePermissions tests and update Travis build configuration
2017-06-15 19:24:00 +01:00
Robbie Averill 8ede35e6bd Update Travis build matrix to include PHP 7.1, SS 3.5, 3.6 and 3.x-dev explicitly 2017-06-13 11:13:48 +12:00
Robbie Averill c02d851500 FIX Remove providePermissions tests
Removed the testProvidePermissions case since it is testing localisations that do not exist, and a localisation that belongs to the framework which should not be tested here.
2017-06-13 11:09:56 +12:00
Rastislav Brandobur 9912285761 added CommentsExtension.COMMENTSTABSET definition to lang subfolder 2017-06-12 23:59:13 +02:00
Robbie Averill f27b357b06 Update translations 2017-05-12 15:06:51 +12:00
Rastislav Brandobur 79e16d5661 Added missing translation to Comments TabSet Title 2017-04-14 17:09:33 +02:00
Paul 2d076e4267 Merge pull request #200 from robbieaverill/bugfix/improve-delete-message
FIX Improve delete reply confirmation message
2017-03-16 09:14:14 +13:00
Robbie Averill 5a22f6faa5 FIX Improve delete reply confirmation message 2017-03-15 15:59:31 +13:00
Daniel Hensby 2ccd8d8af5 Merge pull request #197 from rasstislav/patch-1
added missing _t() function
2017-01-31 21:46:16 +00:00
Rastislav Brandobur b3dac91090 added missing _t() function 2017-01-31 21:06:06 +01:00
Daniel Hensby 5181ac8be3 Merge pull request #196 from robbieaverill/bugfix/fix-ci-builds
FIX Fix CI builds, remove excessive Travis builds matrixes, update composer constraint for gridfield-bulk-tools
2017-01-27 10:42:02 +00:00
Robbie Averill f8d5b27d50 Remove @dev from gridfield bulk editing tools constraint 2017-01-27 23:23:56 +13:00
Robbie Averill fc4d78605a FIX Use a CommentableItem as the parent for the test Comment 2017-01-27 16:42:26 +13:00
Robbie Averill d1697606e1 FIX Change BaseClass to ParentClass 2017-01-27 16:20:14 +13:00
Robbie Averill d1b947afbe FIX Use test database in GridFieldAction test class 2017-01-27 15:56:19 +13:00
Robbie Averill 07586d7579 FIX Fix CI builds, remove excessive Travis builds matrixes, update composer constraint for gridfield-bulk-tools 2017-01-27 12:48:57 +13:00
Damian Mooyman 19224d9813 Merge pull request #195 from robbieaverill/feature/ss4-compat
SilverStripe 4 compatibility
2017-01-25 17:52:20 +13:00
Damian Mooyman 978879e01c Fix incorrect namespace 2017-01-25 17:51:21 +13:00
Robbie Averill d4e58316e5 Merge pull request #1 from andrewandante/patch-1
FIX namespacing for BulkManager
2017-01-20 06:16:51 +13:00
Andrew Aitken-Fincham 279d47f43c FIX namespacing for BulkManager
This namespace changes once BulkManager gets updated to ss4.
2017-01-19 13:23:00 +00:00
Robbie Averill 7a92b0915d Remove deprecated@2.0. Update Migrate task to check table name. Minor tweaks to class names. Use Director::absoluteURL. 2017-01-17 17:39:06 +13:00
Robbie Averill 6aeab9f823 FIX Update upgrade mapping, add purifier back into composer and travis build 2017-01-17 16:56:30 +13:00
Robbie Averill ab3f35257b SS4 compatibility updates - ORM, Form, Controller changes
* Remove CommentList and replace with a polymorphic has_one relationship
* Tweaks for unit tests. Add tests for encode/decodeClassName.
2017-01-17 16:25:09 +13:00
Robbie Averill c698632aae SS4 compat: Update composer and YAML config, rename classes, update readme 2017-01-16 18:10:05 +13:00
Damian Mooyman f74b13a2b7 Update translations 2016-11-17 12:39:49 +13:00
Damian Mooyman 86e6d05bf5 Remove obsolete branch-alias 2016-11-17 10:17:57 +13:00
Daniel Hensby 2b8ff849ed Merge pull request #191 from intwebg/patch-3
add REPLYTO for french language
2016-11-07 16:16:52 +00:00
Will Rossiter 578845bd19 Merge pull request #190 from intwebg/patch-2
Translation of < Reply to > to other language
2016-10-21 07:51:00 +13:00
Stéphane Guevremont 9cb935203a add REPLYTO for french language
see CommentsInterface_singlecomment_ss for my previous add
2016-10-20 13:40:23 -04:00
Stéphane Guevremont b5557a2a51 Translation of < Reply to > to other language 2016-10-20 13:31:31 -04:00
Damian Mooyman 09db00a783 Update translations 2016-08-17 11:08:00 +12:00
Daniel Hensby 3541dd6002 Merge pull request #185 from janneklouman/master
Extended translation support
2016-07-05 11:18:43 +01:00
Janne Klouman b34a41e828 Added $usesDatabase = true to CommentAdminTest 2016-07-05 10:26:08 +02:00
Janne Klouman 821a6dc383 Extended translation support
Added possibility to translate 'Options' field label in comment CMS fields. 
Fixed Name field label trying to get non-existent 'AuthorName' field label (see line 193).
2016-06-10 16:04:03 +02:00
Janne Klouman 6822ecd669 Extended translation support
Added translation support for CommentsAdmin's bulk action labels (shown in drop down).
2016-06-10 15:08:45 +02:00
Janne Klouman 801025c39a Extended translation support
Added translation support for the CommentsAdmin's GridField's inline spam/approve marking labels, and also for the success messages (controller response).
2016-06-10 15:02:28 +02:00
Damian Mooyman 1a94d1cd55 Merge pull request #184 from silverstripe/add-helpul-robot-badge
Added Helpful Robot badge
2016-06-07 13:59:25 +12:00
Christopher Pitt a365495226 Added Helpful Robot badge 2016-05-21 17:34:11 +12:00
Damian Mooyman 93ddc3cc08 Update changelog for 2.1.1 2016-05-18 17:03:24 +12:00
Damian Mooyman 55d0bd62ce Update translations 2016-05-18 16:58:43 +12:00
Damian Mooyman ebc1728bf8 Alias master as 2.1 2016-05-18 15:50:05 +12:00
Damian Mooyman 7cbabe33ad Add changelog for 2.1.0 2016-05-18 15:49:04 +12:00
Daniel Hensby 2ce4d2a5f7 Merge pull request #183 from tractorcow/pulls/fix-missing-hash
BUG Fix missing hash change for #177
2016-04-12 14:33:25 +01:00
Damian Mooyman ad78ac176c BUG Fix missing hash change for #177 2016-04-12 12:07:37 +12:00
Damian Mooyman 9f527ef54d Merge pull request #182 from oddnoc/patch-1
BUG: Fix infinite loop in requireDefaultRecords()
2016-03-31 17:49:44 +13:00
Fred Condo 39cc3101a6 BUG: Fix infinite loop in requireDefaultRecords()
Calling nextRecord() returns the first record again and again, resulting in an infinite loop. Call next() instead.
2016-03-24 13:09:05 -07:00
Daniel Hensby bb0de16f36 Merge pull request #177 from gordonbanderson/168-fix-moderation-url
FIX: Redirect moderation to #moderation instead of #PostCommentForm_error
2016-03-24 09:46:46 +00:00
Gordon Anderson 3610414ed0 FIX: Redirect to a URL that does not indicate an error, style moderation method 2016-03-24 08:55:23 +07:00
Daniel Hensby f187a02a72 Merge pull request #180 from tractorcow/pulls/psr2
Reformat as PSR-2
2016-03-23 21:53:42 +00:00
Damian Mooyman e42a4c5c81 Merge pull request #141 from helpfulrobot/add-standard-scrutinizer-config
Added standard Scrutinizer config
2016-03-21 17:35:30 +13:00
Damian Mooyman 02d5485f18 Merge pull request #181 from scott1702/gravatar
Update docs about gravatars
2016-03-21 17:28:44 +13:00
helpfulrobot b854dab635 Added standard Scrutinizer config 2016-03-21 17:08:53 +13:00
scott1702 5d99054805 Update docs about gravatars 2016-03-21 13:33:44 +13:00
Daniel Hensby af60346dfe Merge pull request #175 from gordonbanderson/gravatar
FIX: Layout with Gravatar fixed
2016-03-21 00:29:27 +00:00
Damian Mooyman 4b31713128 Reformat as PSR-2 2016-02-19 13:48:25 +13:00
Damian Mooyman c2fee6bd33 Merge pull request #167 from gordonbanderson/issue_162
ENHANCEMENT: Ressurect AJAX spam/ham/approve
2016-02-16 09:43:26 +13:00
Damian Mooyman b1bf62d49c Merge pull request #166 from gordonbanderson/nonjsspamredirection
FIX: Non JS spam/ham/approve now redirect back to relevant comment
2016-02-16 09:23:02 +13:00
Gordon Anderson 152137d475 FIX: Layout with Gravatar fixed 2016-02-15 17:05:17 +07:00
Gordon Anderson 8ef9183457 FIX: Non JS spam/ham/approve now redirect back to relevant comment 2016-02-15 16:46:05 +07:00
Gordon Anderson fd153265e9 ENHANCEMENT: Ajax spam/ham/approve/delete ressurected. Added confirm box for delete 2016-02-15 16:38:17 +07:00
Damian Mooyman 7721c20e80 Merge pull request #172 from gordonbanderson/issue170_can_reply_spam
FIX: Take account of spam/moderation status when enabling replies
2016-02-15 11:35:32 +13:00
Damian Mooyman aaa95e0c4c Merge pull request #164 from gordonbanderson/issue_163
Minor: Turn off line numbers for generated CSS
2016-02-15 10:43:24 +13:00
Gordon Anderson 551841fbda FIX: Take account of spam/moderation status when enabling replies to a comment 2016-02-13 21:47:50 +07:00
Gordon Anderson a3d71cb1b7 Minor: Turn off line numbers for generated CSS 2016-02-12 21:14:43 +07:00
Damian Mooyman 5ec6724393 Merge pull request #160 from gordonbanderson/readme_notifications_package
Update version of related package in README
2016-02-10 11:11:30 +13:00
Damian Mooyman 96f93c0e0e Merge pull request #161 from gordonbanderson/issue_159
FIX: Move ID for permalink to start of comment
2016-02-10 11:09:25 +13:00
Damian Mooyman 788b6fa247 Merge pull request #157 from gordonbanderson/issue_156_nested_docs
FIX: Add missing parameters for nested comments to example configuration
2016-02-10 10:52:30 +13:00
Gordon Anderson 7152fbc235 FIX: When viewing a comment permalink full comment and posting data shows 2016-02-09 20:47:04 +07:00
Gordon Anderson c730a731f3 MINOR: Update version of notifications module to one that is not deprecated 2016-02-09 20:07:22 +07:00
Gordon Anderson 41bc663d5d FIX: Add missing parameters for nested comments to example configuration 2016-02-09 13:46:31 +07:00
Damian Mooyman 384979662a Add changelog for 2.0.3 2016-02-04 12:00:02 +13:00
Damian Mooyman 7d07572994 Update translations 2016-02-04 11:03:41 +13:00
Daniel Hensby 1d63b8d323 Fixing Travis support repo branch 2016-01-19 20:20:18 +00:00
Daniel Hensby 1243d0d848 Fixing travis config pointing to wrong repo 2016-01-19 16:26:04 +00:00
Gordon Anderson 43a2414f50 FIX: Badges now pointing at SilverStripe master 2016-01-19 16:11:49 +00:00
Damian Mooyman 0e64cc4fe0 Merge pull request #154 from gordonbanderson/improvecoverage
ENHANCEMENT: Increase test coverage from 54% to 92%
2016-01-18 17:12:47 +13:00
Gordon Anderson 450ed93f36 FIX: Change creation of CreatedField to unchained as setName() method of DatetimeField is not chainable 2016-01-17 16:15:07 +07:00
Gordon Anderson 8dc36df7a4 FIX: The AutoFormat.AutoParagraph injector of HTMLPurifier fails if the p tag is not allowed. Make a check for this 2016-01-17 16:15:07 +07:00
Gordon Anderson eba88fe869 ENHANCEMENT: Increase test coverage from 54% to 92% 2016-01-17 16:15:07 +07:00
Damian Mooyman a177b49c35 Merge pull request #153 from gordonbanderson/issue151_2
Add CMS to Suggest Section of Composer File
2016-01-13 16:52:10 +13:00
Gordon Anderson 277a917bf5 FIX: Added SilverStripe CMS to suggested packages. Fixed value to be description for html purifier instead of a version constraint 2016-01-13 10:17:24 +07:00
Damian Mooyman 72a7899e3d Merge pull request #150 from helpfulrobot/update-license-year
Updated license year
2016-01-05 11:15:44 +13:00
helpfulrobot ca22a337b3 Updated license year 2016-01-01 06:42:13 +13:00
Daniel Hensby 167e4dd02a Merge pull request #149 from helpfulrobot/add-standard-code-of-conduct
Added standard code of conduct
2015-11-21 12:27:45 +00:00
helpfulrobot f40793723b Added standard code of conduct 2015-11-21 20:12:57 +13:00
Daniel Hensby d0a4aa9bfb Merge pull request #143 from helpfulrobot/add-standard-travis-config
Added standard Travis config
2015-11-20 15:50:34 +00:00
Daniel Hensby d403e0437e Merge pull request #145 from helpfulrobot/add-standard-editor-config
Added standard editor config
2015-11-20 15:07:48 +00:00
Daniel Hensby 8ee4caaef6 Merge pull request #146 from helpfulrobot/add-standard-license
Added standard license
2015-11-19 17:26:07 +00:00
Daniel Hensby 061064468c Merge pull request #147 from helpfulrobot/add-license-to-composer
Added license to composer.json
2015-11-19 10:48:40 +00:00
Daniel Hensby dc12833404 Merge pull request #148 from helpfulrobot/add-standard-git-attributes
Added standard git attributes
2015-11-19 10:40:23 +00:00
helpfulrobot 64170cfcff Added standard git attributes 2015-11-19 19:11:24 +13:00
helpfulrobot a29591deb0 Added license to composer.json 2015-11-19 18:51:40 +13:00
helpfulrobot 26f731573f Added standard license 2015-11-19 18:30:02 +13:00
Scott Hutchinson 04b95cb943 Merge pull request #144 from tractorcow/pulls/release-202
Add changelog for 2.0.2
2015-11-19 16:34:57 +13:00
helpfulrobot 0a72258149 Added standard Travis config 2015-11-19 14:18:53 +13:00
helpfulrobot 0ebdc10f02 Added standard editor config 2015-11-19 13:24:59 +13:00
Damian Mooyman 435618a07d Add changelog for 2.0.2 2015-11-18 15:53:35 +13:00
Damian Mooyman ed1030194f Update translations 2015-11-17 16:07:37 +13:00
Damian Mooyman e8ba40f42c Merge pull request #140 from silverstripe/prevent-duplicate-ids-on-action-buttons
Prevented duplicate IDs on action buttons
2015-11-17 13:22:53 +13:00
Christopher Pitt 671766364b Prevented duplicate IDs on action buttons 2015-11-17 13:12:21 +13:00
Christopher Pitt 4d538f565c Merge pull request #139 from tractorcow/pulls/fix-dupe-state
BUG Fix each gridfield having triple gridstate components
2015-11-17 12:55:23 +13:00
Damian Mooyman e26e4d916b BUG Fix each gridfield having triple gridstate components 2015-11-17 12:47:07 +13:00
Will Rossiter ac92dbb515 Handle when extension has been removed from object
When in the admin panel, if parent has been removing from commenting then it won't have the commenting extension attached.
2015-11-16 15:35:51 +13:00
Damian Mooyman 018881c345 Merge pull request #138 from silverstripe/remove-deprecated-example-configuration-2
Removed deprecated example configuration
2015-11-16 12:04:18 +13:00
Christopher Pitt c6c4702caf Removed deprecated example configuration 2015-11-16 11:56:34 +13:00
Scott Hutchinson 877748b25a Merge pull request #137 from tractorcow/pulls/travis
Test with 3.2 and php 5.6
2015-10-30 18:38:15 +13:00
Damian Mooyman b989aab588 Test with 3.2 and php 5.6 2015-10-30 14:49:20 +13:00
Christopher Pitt 31dd95a76d Merge pull request #136 from tractorcow/pulls/fix-defaults
BUG Fix assignment of default values
2015-08-26 10:47:06 +12:00
Damian Mooyman 752d509d1e BUG Fix assignment of default values
Fixes #130
2015-08-25 16:49:58 +12:00
Damian Mooyman d3a9e78002 Update translations 2015-08-20 11:13:22 +12:00
Daniel Hensby 9d0a4ebb5e Move to new travis containerised infrastructure 2015-07-20 16:02:57 +01:00
Christopher Pitt bc8f2c1ad7 Merge pull request #125 from tractorcow/pulls/2.0/fix-spam-redirection
BUG Fix redirection on moderated & spam comments
2015-05-29 09:25:30 +12:00
Damian Mooyman 74c14a81c7 Merge pull request #133 from assertchris/update-translations
Updated translations
2015-05-20 09:48:10 +12:00
Christopher Pitt 1432fe1cab Updated translations 2015-05-20 09:29:41 +12:00
Damian Mooyman 9ee9c5bdca Merge pull request #132 from assertchris/add-better-scrutinizer-support
Added better Scrutinizer support
2015-05-14 11:49:37 +12:00
Christopher Pitt 3e4786c312 Added better Scrutinizer support 2015-05-13 00:10:50 +12:00
Damian Mooyman 832fc8685a Merge pull request #129 from assertchris/use-hash-shim
Use hash_* shim
2015-05-06 11:33:45 +12:00
Christopher Pitt a31fc98b38 Use hash_* shim 2015-05-06 10:55:10 +12:00
Damian Mooyman 297fb8666d Merge pull request #126 from gordonbanderson/patch-1
Update syntax error in Configuration.md
2015-05-05 09:08:37 +12:00
Gordon Anderson 563a617b14 Update syntax error in Configuration.md
Minor error in the example yml file in the documentation, namely a semicolon instead of a colon in the yml file
2015-05-04 20:13:44 +07:00
Damian Mooyman 2aa8d974b0 BUG Fix redirection on moderated & spam comments 2015-04-30 14:34:57 +12:00
Christopher Pitt 304dee2792 Merge pull request #123 from tractorcow/pulls/2.0/fix-spam-moderation
BUG Fix "your comment is awaiting moderation" and "your comment is rejected as spam" messages appearing together.
2015-04-24 15:16:49 +12:00
Damian Mooyman d0cd57104d BUG FIx "your comment is awaiting moderation" and "your comment is rejected as spam" messages appearing together. 2015-04-24 14:28:40 +12:00
Damian Mooyman 651a4e4977 Merge branch 'pr/121' 2015-04-23 11:50:30 +12:00
Daniel Caine e8e5abdc23 Cleanup of comments theme 2015-04-23 11:49:47 +12:00
Christopher Pitt 8d987e3336 Merge pull request #122 from tractorcow/pulls/paren-comment-context
API Add readonly parent details in comment form in CMS
2015-04-23 10:09:16 +12:00
Damian Mooyman f772d32d55 API Add readonly parent details in comment form in CMS 2015-04-22 17:00:36 +12:00
Christopher Pitt 24aa0f938f Merge pull request #119 from tractorcow/pulls/fix-comment-visibility
BUG Fix comments tab appearing on pages without comments enabled
2015-04-22 15:44:56 +12:00
Damian Mooyman 138bbbfd84 BUG Fix comments tab appearing on pages without comments enabled 2015-04-22 14:25:48 +12:00
Damian Mooyman ad93c5c3ab Fix merge conflict 2015-04-21 19:34:04 +12:00
Damian Mooyman 298ac89ad6 Merge pull request #118 from scott1702/parent-comment
Remove Parent Comment row
2015-04-21 18:45:56 +12:00
scott1702 a5bed7f27f Remove parent comment field 2015-04-21 16:31:02 +12:00
Christopher Pitt 614da962ce Merge pull request #115 from tractorcow/pulls/2.0/nested-comments
API Nested comments feature (final)
2015-04-21 14:45:54 +12:00
Damian Mooyman a0974dd399 Merge pull request #117 from assertchris/add-spam-row-highlight
Updated for new GridField API
2015-04-21 11:47:10 +12:00
Damian Mooyman 468df23801 Nested comments proof of concept
Update theme to new look and feel
Add jquery-validate 1.3.1 (w/ nz dst patch)
2015-04-21 11:12:42 +12:00
Christopher Pitt adabc623f6 Updated for new GridField API 2015-04-21 09:28:11 +12:00
Damian Mooyman 75be840dce Merge pull request #114 from assertchris/add-spam-row-highlight
Add spam row highlight
2015-04-20 16:23:00 +12:00
Christopher Pitt 5c71b2f141 Add spam row highlight 2015-04-20 10:03:45 +12:00
Christopher Pitt 0c698a813a Merge pull request #113 from tractorcow/pulls/2.0/comment-gridfield-actions
BUG Only show correct gridfield options for comments
2015-04-16 15:47:16 +12:00
Damian Mooyman 85653fea1e BUG Only show correct gridfield options for comments 2015-04-16 15:37:42 +12:00
Damian Mooyman 26361cccba Merge pull request #112 from assertchris/change-moderation-labels
Change moderation labels
2015-04-16 15:18:20 +12:00
Christopher Pitt 086dd03b5c Change moderation labels 2015-04-16 14:50:56 +12:00
Christopher Pitt 1410702fce Merge pull request #110 from tractorcow/pr/109
Fixed comment permissions
2015-04-16 11:10:19 +12:00
Christopher Pitt a004dfe780 Fixed comment permissions 2015-04-16 11:01:34 +12:00
Christopher Pitt 7585b5d19f Merge pull request #108 from tractorcow/pulls/2.0/frontend-moderation
API Restore ability to do frontend-moderation
2015-04-14 14:09:35 +12:00
Damian Mooyman 3deea94a8c API Restore ability to do frontend-moderation
This feature is disabled by default, but can be turned on either for spam, unmoderated comments, or both.
2015-04-14 14:04:29 +12:00
Damian Mooyman eadde16232 Merge pull request #107 from assertchris/add-new-moderation-lists
[Review/Feedback] Add new moderation lists
2015-04-14 13:04:55 +12:00
Christopher Pitt 162ad33e26 Adding new moderation lists 2015-04-14 12:57:30 +12:00
Christopher Pitt 128fa67f65 Merge pull request #106 from tractorcow/pulls/2.0/fix-login-options
BUG Fix comments form appearing on login page
2015-04-13 18:48:59 +12:00
Damian Mooyman 69c928650a BUG Fix comments form appearing on login page 2015-04-13 16:34:20 +12:00
Christopher Pitt 5254d72ccd Merge pull request #105 from tractorcow/pulls/2.0/config-moderation-options
API Allow commenting options to be set per page
2015-04-13 15:39:40 +12:00
Damian Mooyman 6ebbd9e1ac API Allow commenting options to be set per-page in CMS 2015-04-13 15:35:25 +12:00
Sam Minnée f9c93ef9aa Merge pull request #104 from tractorcow/pulls/2.0/config-api
API Move configuration of comments to the config API and enable per-instance configuration
2015-04-13 15:23:40 +12:00
Damian Mooyman 1900ab14b5 API Move configuration of comments to the config API and enable per-instance configuration
Enable comment moderation per-page
2015-04-09 10:34:33 +12:00
Christopher Pitt 93414c5231 Merge pull request #103 from tractorcow/pulls/better-redirect
Better redirection on posting of comment
2015-04-02 12:02:29 +13:00
Daniel Hensby 882c65fdd5 Merge pull request #97 from arambalakjian/pulls/controller-instantiation
API Adding config option for enabling comments to be added to SiteTree
2015-04-01 13:04:54 +01:00
Damian Mooyman 0b49de3648 ENHANCEMENT If a user posts a spamcomment and it's saved, ensure the form is correctly redirected to 2015-04-01 17:12:47 +13:00
Christopher Pitt e576717c26 Merge pull request #102 from tractorcow/pulls/fix-action-nullification
BUG Workaround for https://github.com/silverstripe/silverstripe-framework/pull/4053
2015-04-01 16:54:35 +13:00
Damian Mooyman 26d0212ef3 BUG Workaround for https://github.com/silverstripe/silverstripe-framework/pull/4053 2015-04-01 15:29:01 +13:00
Christopher Pitt a41a0054e2 Merge pull request #101 from tractorcow/pulls/ux-redirection
UX Improvements
2015-03-31 08:36:47 +13:00
Damian Mooyman b5a53451b3 UX Improvements
Redirect to login form if lacking permissions
Better redirectBack if we can guess from the comment
2015-03-30 18:57:29 +13:00
Christopher Pitt 974b4554fb Merge pull request #99 from tractorcow/pulls/better-xss-protection
Better XSS Protection via hashed token
2015-03-30 15:30:21 +13:00
Damian Mooyman 9087261654 Better XSS Protection via hashed token
Allows moderation links to be generated for users other than the currently logged in user, as it doesn't rely on the current session.
2015-03-30 14:56:28 +13:00
Aram Balakjian 00cb672bf5 Changed instantiation of controller to ::create 2015-03-23 17:58:53 +00:00
Aram Balakjian 99cf501393 NEW Only add comments to SiteTree objects if needed 2015-03-23 16:59:05 +00:00
Will Rossiter 2a20037e49 Merge pull request #92 from tractorcow/pulls/fix-temp-files
Provide better temporary location for HTMLPurifier cache files
2015-01-12 19:11:16 +13:00
Damian Mooyman 7f1005ed06 Provide better temporary location for HTMLPurifier cache files 2015-01-12 14:00:20 +13:00
Will Rossiter c73f9f48f0 Merge pull request #88 from tractorcow/pulls/fix-deps
Update composer to point to now fixed 2.1 branch of bulk editing tools
2014-12-15 15:34:13 +13:00
Will Rossiter 589e2e4660 Merge pull request #90 from dhensby/master
Adding .editorconfig
2014-12-05 15:22:31 +13:00
Daniel Hensby 7f05c0039c Adding .editorconfig 2014-12-04 15:47:12 +00:00
Damian Mooyman 1d315983a4 Update composer to point to now fixed 2.1 branch of bulk editing tools 2014-11-03 09:50:46 +13:00
199 changed files with 19234 additions and 2557 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
# For more information about the properties used in
# this file, please see the EditorConfig documentation:
# http://editorconfig.org/
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,js,json,css,scss,feature}]
indent_size = 2
indent_style = space
[composer.json]
indent_size = 4
# Don't perform any clean-up on thirdparty files
[thirdparty/**]
trim_trailing_whitespace = false
insert_final_newline = false

8
.eslintignore Normal file
View File

@ -0,0 +1,8 @@
# Ignore dist files
client/dist/
# Ignore legacy files
client/src/legacy/
# Ignore auto-generated language files
client/lang/

1
.eslintrc.js Normal file
View File

@ -0,0 +1 @@
module.exports = require('@silverstripe/eslint-config/.eslintrc');

7
.gitattributes vendored Normal file
View File

@ -0,0 +1,7 @@
/tests export-ignore
/docs export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/.scrutinizer.yml export-ignore
/codecov.yml export-ignore

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 11:10 AM UTC, only on Wednesday and Thursday
schedule:
- cron: '10 11 * * 3,4'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

17
.github/workflows/update-js.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Update JS
on:
workflow_dispatch:
# Run on a schedule of once per quarter
schedule:
- cron: '0 0 1 */3 *'
jobs:
update-js:
name: Update JS
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Update JS
uses: silverstripe/gha-update-js@v1

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.sass-cache
/.php_cs.cache
/node_modules/

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
10

View File

@ -1,23 +0,0 @@
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
language: php
php:
- 5.3
env:
- DB=MYSQL CORE_RELEASE=3.1
- DB=MYSQL CORE_RELEASE=master
- DB=PGSQL CORE_RELEASE=3.1
matrix:
include:
- php: 5.4
env: DB=MYSQL CORE_RELEASE=master
before_script:
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
- cd ~/builds/ss
script:
- phpunit comments/tests/

View File

@ -1,8 +1,9 @@
[main]
host = https://www.transifex.com
[silverstripe-comments.master]
[o:silverstripe:p:silverstripe-comments:r:master]
file_filter = lang/<lang>.yml
source_file = lang/en.yml
source_lang = en
type = YML
type = YML

25
.upgrade.yml Normal file
View File

@ -0,0 +1,25 @@
mappings:
CommentAdmin: SilverStripe\Comments\Admin\CommentAdmin
CommentsGridField: SilverStripe\Comments\Admin\CommentsGridField
CommentsGridFieldAction: SilverStripe\Comments\Admin\CommentsGridFieldAction
CommentsGridFieldBulkAction_Handlers: SilverStripe\Comments\Admin\CommentsGridFieldBulkAction\Handlers
CommentsGridFieldConfig: SilverStripe\Comments\Admin\CommentsGridFieldConfig
CommentingController: SilverStripe\Comments\Controllers\CommentingController
CommentsExtension: SilverStripe\Comments\Extensions\CommentsExtension
Comment: SilverStripe\Comments\Model\Comment
Comment_SecurityToken: SilverStripe\Comments\Model\Comment\SecurityToken
CommentAdminTest: SilverStripe\Comments\Tests\CommentAdminTest
CommentingControllerTest: SilverStripe\Comments\Tests\CommentingControllerTest
CommentingTest: SilverStripe\Comments\Tests\CommentingTest
CommentListTest: SilverStripe\Comments\Tests\CommentListTest
CommentsExtensionTest: SilverStripe\Comments\Tests\CommentsExtensionTest
CommentsGridFieldActionTest: SilverStripe\Comments\Tests\CommentsGridFieldActionTest
CommentsGridFieldBulkActionTest: SilverStripe\Comments\Tests\CommentsGridFieldBulkActionTest
CommentsGridFieldConfigTest: SilverStripe\Comments\Tests\CommentsGridFieldConfigTest
CommentsGridFieldTest: SilverStripe\Comments\Tests\CommentsGridFieldTest
CommentsTest: SilverStripe\Comments\Tests\CommentsTest
CommentableItem: SilverStripe\Comments\Tests\Stubs\CommentableItem
CommentableItemEnabled: SilverStripe\Comments\Tests\Stubs\CommentableItemEnabled
CommentableItemDisabled: SilverStripe\Comments\Tests\Stubs\CommentableItemDisabled
CommentableItem_Controller: SilverStripe\Comments\Tests\Stubs\CommentableItemController
CommentTestHelper: SilverStripe\Comments\Tests\CommentTestHelper

17
LICENSE
View File

@ -1,17 +0,0 @@
Copyright (c) 2007-2010, SilverStripe Limited - www.silverstripe.com
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of SilverStripe nor the names of its contributors may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.

View File

@ -1,31 +1,31 @@
# Comments
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-comments.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-comments)
## Maintainers
* Will Rossiter (Nickname: willr, wrossiter)
<will at silverstripe dot com>
[![CI](https://github.com/silverstripe/silverstripe-comments/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-comments/actions/workflows/ci.yml)
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Introduction
This module provides commenting functionality for Pages and other DataObjects
on your SilverStripe site.
This module provides commenting functionality for Pages and other DataObjects on your Silverstripe site.
For more documentation about the module see the provided documentation located
inside the docs folder.
For more documentation about the module see the provided documentation located inside the docs folder.
## Requirements
* SilverStripe 3.1
* Silverstripe ^4.0
**Note:** This branch is compatible with Silverstripe 4. For a Silverstripe 3 release, please see the 2.x release line.
## Installation
```
composer require silverstripe/comments
```
See docs/en/Installing.md
## Related
* [tractorcow/silverstripe-comments-notifications](https://github.com/tractorcow/silverstripe-comments-notifications): Comment admin email notifications module
* [silverstripe/comment-notifications](https://github.com/silverstripe/comment-notifications): Comment admin email notifications module
## Contributing
@ -39,4 +39,4 @@ and any new translations will be merged back to the project source code.
Please use https://www.transifex.com/projects/p/silverstripe-comments/ to contribute translations,
rather than sending pull requests with YAML files.
See the ["i18n" topic](http://doc.silverstripe.org/framework/en/trunk/topics/i18n) on doc.silverstripe.org for more details.
See the ["i18n" topic](https://docs.silverstripe.org/en/4/developer_guides/i18n/) on doc.silverstripe.org for more details.

View File

@ -1,31 +0,0 @@
<?php
/**
* Comments Default Configuration
*
* To enable comments on your own {@link DataObject}'s you need to
* call Commenting::add_comments($object_name, $settings);
*
* Where $object_name is the name of the subclass of DataObject you want
* to add the comments to and $settings is a map of configuration options
* and values
*
* Example: mysite/_config.php
*
* <code>
* // uses the default values
* Commenting::add('SiteTree');
*
* // set configuration
* Commenting::add('SiteTree', array(
* 'require_login' => true
* ));
* </code>
*
* To see all the configuration options read docs/en/Configuration.md or
* consult the Commenting class.
*/
if(class_exists('SiteTree') && !Commenting::has_commenting('SiteTree')) {
Commenting::add('SiteTree');
}

11
_config/comments.yml Normal file
View File

@ -0,0 +1,11 @@
---
# SiteTree specific config for comments
Name: commentssitetree
only:
moduleexists: 'cms'
---
SilverStripe\CMS\Model\SiteTree:
extensions:
comments: SilverStripe\Comments\Extensions\CommentsExtension
comments:
enabled_cms: true

3
_config/legacy.yml Normal file
View File

@ -0,0 +1,3 @@
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
Comment: SilverStripe\Comments\Model\Comment

View File

@ -2,9 +2,6 @@
Name: commentroutes
After: framework/routes#coreroutes
---
Director:
SilverStripe\Control\Director:
rules:
# handle old 2.4 style urls
'CommentingController//$Action/$ID/$OtherID': 'CommentingController'
'PageComments/$Action/$ID/$OtherID': 'CommentingController'
'PageComments_Controller/$Action/$ID/$OtherID': 'CommentingController'
comments: SilverStripe\Comments\Controllers\CommentingController

1
client/dist/js/CommentsInterface.js vendored Normal file
View File

@ -0,0 +1 @@
!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/CommentsInterface.js")}({"./client/src/legacy/CommentsInterface.js":function(e,t,n){"use strict";(function(e){!function(e){e(function(){e.validator.methods.url=function(e,t){return this.optional(t)||/^(?:(?:(?:https?|ftp):)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(e)},e(".comments-holder-container form").each(function(){e(this).validate({ignore:":hidden",errorClass:"required",errorElement:"span",invalidHandler:function(t,n){e("html, body").animate({scrollTop:e(n.errorList[0].element).offset().top-30},200)},errorPlacement:function(e,t){e.addClass("message").insertAfter(t)}})}),e(".comment").children(".info").not(window.document.location.hash).nextAll(".comment-replies-container").children(".comment-reply-form-holder").hide(),e(".comments-holder").on("click",".comment-reply-link",function(t){var n=e(".comment-reply-form-holder"),r="#"+e(this).attr("aria-controls"),o=e(r).closest(".comment-reply-form-holder");e(this).attr("aria-expanded",function(e,t){return"true"==t?"false":"true"}),t.preventDefault(),o.is(":visible")?n.slideUp():(n.not(o).slideUp(),o.slideDown())}),e(".comments-holder .comments-list").on("click","div.comment-moderation-options a",function(t){t.stopPropagation();var n=e(this);if(n.hasClass("delete")){var r=ss.i18n._t("CommentsInterface_singlecomment_ss.DELETE_CONFIRMATION");if(!window.confirm(r))return t.preventDefault(),!1}var o=n.parents(".comment:first");e.ajax({url:e(this).attr("href"),cache:!1,success:function(t){n.hasClass("ham")?(o.html(t),o.removeClass("spam")):n.hasClass("approve")?(o.html(t),o.removeClass("unmoderated")):n.hasClass("delete")?o.fadeOut(1e3,function(){o.remove(),0===e(".comments-holder .comments-list").children().length&&e(".no-comments-yet").show()}):n.hasClass("spam")&&o.html(t).addClass("spam")},failure:function(e){var t=ss.i18n._t("CommentsInterface_singlecomment_ss.AJAX_ERROR");alert(t)}}),t.preventDefault()})})}(e)}).call(t,n(0))},0:function(e,t){e.exports=jQuery}});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","./jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(){function b(a){return a.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}a.validator.addMethod("maxWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length<=d},a.validator.format("Please enter {0} words or less.")),a.validator.addMethod("minWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length>=d},a.validator.format("Please enter at least {0} words.")),a.validator.addMethod("rangeWords",function(a,c,d){var e=b(a),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},a.validator.format("Please enter between {0} and {1} words."))}(),a.validator.addMethod("abaRoutingNumber",function(a){var b=0,c=a.split(""),d=c.length;if(9!==d)return!1;for(var e=0;e<d;e+=3)b+=3*parseInt(c[e],10)+7*parseInt(c[e+1],10)+parseInt(c[e+2],10);return 0!==b&&b%10===0},"Please enter a valid routing number."),a.validator.addMethod("accept",function(b,c,d){var e,f,g,h="string"==typeof d?d.replace(/\s/g,""):"image/*",i=this.optional(c);if(i)return i;if("file"===a(c).attr("type")&&(h=h.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g,"\\$&").replace(/,/g,"|").replace(/\/\*/g,"/.*"),c.files&&c.files.length))for(g=new RegExp(".?("+h+")$","i"),e=0;e<c.files.length;e++)if(f=c.files[e],!f.type.match(g))return!1;return!0},a.validator.format("Please enter a value with a valid mimetype.")),a.validator.addMethod("alphanumeric",function(a,b){return this.optional(b)||/^\w+$/i.test(a)},"Letters, numbers, and underscores only please"),a.validator.addMethod("bankaccountNL",function(a,b){if(this.optional(b))return!0;if(!/^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test(a))return!1;var c,d,e,f=a.replace(/ /g,""),g=0,h=f.length;for(c=0;c<h;c++)d=h-c,e=f.substring(c,c+1),g+=d*e;return g%11===0},"Please specify a valid bank account number"),a.validator.addMethod("bankorgiroaccountNL",function(b,c){return this.optional(c)||a.validator.methods.bankaccountNL.call(this,b,c)||a.validator.methods.giroaccountNL.call(this,b,c)},"Please specify a valid bank or giro account number"),a.validator.addMethod("bic",function(a,b){return this.optional(b)||/^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test(a.toUpperCase())},"Please specify a valid BIC code"),a.validator.addMethod("cifES",function(a,b){"use strict";function c(a){return a%2===0}if(this.optional(b))return!0;var d,e,f,g,h=new RegExp(/^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi),i=a.substring(0,1),j=a.substring(1,8),k=a.substring(8,9),l=0,m=0,n=0;if(9!==a.length||!h.test(a))return!1;for(d=0;d<j.length;d++)e=parseInt(j[d],10),c(d)?(e*=2,n+=e<10?e:e-9):m+=e;return l=m+n,f=(10-l.toString().substr(-1)).toString(),f=parseInt(f,10)>9?"0":f,g="JABCDEFGHI".substr(f,1).toString(),i.match(/[ABEH]/)?k===f:i.match(/[KPQS]/)?k===g:k===f||k===g},"Please specify a valid CIF number."),a.validator.addMethod("cnhBR",function(a){if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var b,c,d,e,f,g,h=0,i=0;if(b=a.charAt(0),new Array(12).join(b)===a)return!1;for(e=0,f=9,g=0;e<9;++e,--f)h+=+(a.charAt(e)*f);for(c=h%11,c>=10&&(c=0,i=2),h=0,e=0,f=1,g=0;e<9;++e,++f)h+=+(a.charAt(e)*f);return d=h%11,d>=10?d=0:d-=i,String(c).concat(d)===a.substr(-2)},"Please specify a valid CNH number"),a.validator.addMethod("cnpjBR",function(a,b){"use strict";if(this.optional(b))return!0;if(a=a.replace(/[^\d]+/g,""),14!==a.length)return!1;if("00000000000000"===a||"11111111111111"===a||"22222222222222"===a||"33333333333333"===a||"44444444444444"===a||"55555555555555"===a||"66666666666666"===a||"77777777777777"===a||"88888888888888"===a||"99999999999999"===a)return!1;for(var c=a.length-2,d=a.substring(0,c),e=a.substring(c),f=0,g=c-7,h=c;h>=1;h--)f+=d.charAt(c-h)*g--,g<2&&(g=9);var i=f%11<2?0:11-f%11;if(i!==parseInt(e.charAt(0),10))return!1;c+=1,d=a.substring(0,c),f=0,g=c-7;for(var j=c;j>=1;j--)f+=d.charAt(c-j)*g--,g<2&&(g=9);return i=f%11<2?0:11-f%11,i===parseInt(e.charAt(1),10)},"Please specify a CNPJ value number"),a.validator.addMethod("cpfBR",function(a,b){"use strict";if(this.optional(b))return!0;if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var c,d,e,f,g=0;if(c=parseInt(a.substring(9,10),10),d=parseInt(a.substring(10,11),10),e=function(a,b){var c=10*a%11;return 10!==c&&11!==c||(c=0),c===b},""===a||"00000000000"===a||"11111111111"===a||"22222222222"===a||"33333333333"===a||"44444444444"===a||"55555555555"===a||"66666666666"===a||"77777777777"===a||"88888888888"===a||"99999999999"===a)return!1;for(f=1;f<=9;f++)g+=parseInt(a.substring(f-1,f),10)*(11-f);if(e(g,c)){for(g=0,f=1;f<=10;f++)g+=parseInt(a.substring(f-1,f),10)*(12-f);return e(g,d)}return!1},"Please specify a valid CPF number"),a.validator.addMethod("creditcard",function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},"Please enter a valid credit card number."),a.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&(/^(5[12345])/.test(a)||/^(2[234567])/.test(a))?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:!!(128&d)},"Please enter a valid credit card number."),a.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=!!e||c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency"),a.validator.addMethod("dateFA",function(a,b){return this.optional(b)||/^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(a)},a.validator.messages.date),a.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(Date.UTC(f,e-1,d,12,0,0,0)),h=g.getUTCFullYear()===f&&g.getUTCMonth()===e-1&&g.getUTCDate()===d):h=!1,this.optional(b)||h},a.validator.messages.date),a.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},a.validator.messages.date),a.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp("\\.("+c+")$","i"))},a.validator.format("Please enter a value with a valid extension.")),a.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number"),a.validator.addMethod("greaterThan",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-greaterThan-blur").length&&e.addClass("validate-greaterThan-blur").on("blur.validate-greaterThan",function(){a(c).valid()}),b>e.val()},"Please enter a greater value."),a.validator.addMethod("greaterThanEqual",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-greaterThanEqual-blur").length&&e.addClass("validate-greaterThanEqual-blur").on("blur.validate-greaterThanEqual",function(){a(c).valid()}),b>=e.val()},"Please enter a greater value."),a.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="",q=5;if(l.length<q)return!1;if(c=l.substring(0,2),h={AL:"\\d{8}[\\dA-Z]{16}",AD:"\\d{8}[\\dA-Z]{12}",AT:"\\d{16}",AZ:"[\\dA-Z]{4}\\d{20}",BE:"\\d{12}",BH:"[A-Z]{4}[\\dA-Z]{14}",BA:"\\d{16}",BR:"\\d{23}[A-Z][\\dA-Z]",BG:"[A-Z]{4}\\d{6}[\\dA-Z]{8}",CR:"\\d{17}",HR:"\\d{17}",CY:"\\d{8}[\\dA-Z]{16}",CZ:"\\d{20}",DK:"\\d{14}",DO:"[A-Z]{4}\\d{20}",EE:"\\d{16}",FO:"\\d{14}",FI:"\\d{14}",FR:"\\d{10}[\\dA-Z]{11}\\d{2}",GE:"[\\dA-Z]{2}\\d{16}",DE:"\\d{18}",GI:"[A-Z]{4}[\\dA-Z]{15}",GR:"\\d{7}[\\dA-Z]{16}",GL:"\\d{14}",GT:"[\\dA-Z]{4}[\\dA-Z]{20}",HU:"\\d{24}",IS:"\\d{22}",IE:"[\\dA-Z]{4}\\d{14}",IL:"\\d{19}",IT:"[A-Z]\\d{10}[\\dA-Z]{12}",KZ:"\\d{3}[\\dA-Z]{13}",KW:"[A-Z]{4}[\\dA-Z]{22}",LV:"[A-Z]{4}[\\dA-Z]{13}",LB:"\\d{4}[\\dA-Z]{20}",LI:"\\d{5}[\\dA-Z]{12}",LT:"\\d{16}",LU:"\\d{3}[\\dA-Z]{13}",MK:"\\d{3}[\\dA-Z]{10}\\d{2}",MT:"[A-Z]{4}\\d{5}[\\dA-Z]{18}",MR:"\\d{23}",MU:"[A-Z]{4}\\d{19}[A-Z]{3}",MC:"\\d{10}[\\dA-Z]{11}\\d{2}",MD:"[\\dA-Z]{2}\\d{18}",ME:"\\d{18}",NL:"[A-Z]{4}\\d{10}",NO:"\\d{11}",PK:"[\\dA-Z]{4}\\d{16}",PS:"[\\dA-Z]{4}\\d{21}",PL:"\\d{24}",PT:"\\d{21}",RO:"[A-Z]{4}[\\dA-Z]{16}",SM:"[A-Z]\\d{10}[\\dA-Z]{12}",SA:"\\d{2}[\\dA-Z]{18}",RS:"\\d{18}",SK:"\\d{20}",SI:"\\d{15}",ES:"\\d{20}",SE:"\\d{20}",CH:"\\d{5}[\\dA-Z]{12}",TN:"\\d{20}",TR:"\\d{5}[\\dA-Z]{17}",AE:"\\d{3}\\d{16}",GB:"[A-Z]{4}\\d{14}",VG:"[\\dA-Z]{4}\\d{16}"},g=h[c],"undefined"!=typeof g&&(i=new RegExp("^[A-Z]{2}\\d{2}"+g+"$",""),!i.test(l)))return!1;for(d=l.substring(4,l.length)+l.substring(0,4),j=0;j<d.length;j++)e=d.charAt(j),"0"!==e&&(n=!1),n||(m+="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(e));for(k=0;k<m.length;k++)f=m.charAt(k),p=""+o+f,o=p%97;return 1===o},"Please specify a valid IBAN"),a.validator.addMethod("integer",function(a,b){return this.optional(b)||/^-?\d+$/.test(a)},"A positive or negative non-decimal number please"),a.validator.addMethod("ipv4",function(a,b){return this.optional(b)||/^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test(a)},"Please enter a valid IP v4 address."),a.validator.addMethod("ipv6",function(a,b){return this.optional(b)||/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(a)},"Please enter a valid IP v6 address."),a.validator.addMethod("lessThan",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-lessThan-blur").length&&e.addClass("validate-lessThan-blur").on("blur.validate-lessThan",function(){a(c).valid()}),b<e.val()},"Please enter a lesser value."),a.validator.addMethod("lessThanEqual",function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-lessThanEqual-blur").length&&e.addClass("validate-lessThanEqual-blur").on("blur.validate-lessThanEqual",function(){a(c).valid()}),b<=e.val()},"Please enter a lesser value."),a.validator.addMethod("lettersonly",function(a,b){return this.optional(b)||/^[a-z]+$/i.test(a)},"Letters only please"),a.validator.addMethod("letterswithbasicpunc",function(a,b){return this.optional(b)||/^[a-z\-.,()'"\s]+$/i.test(a)},"Letters or punctuation only please"),a.validator.addMethod("maxfiles",function(b,c,d){return!!this.optional(c)||!("file"===a(c).attr("type")&&c.files&&c.files.length>d)},a.validator.format("Please select no more than {0} files.")),a.validator.addMethod("maxsize",function(b,c,d){if(this.optional(c))return!0;if("file"===a(c).attr("type")&&c.files&&c.files.length)for(var e=0;e<c.files.length;e++)if(c.files[e].size>d)return!1;return!0},a.validator.format("File size must not exceed {0} bytes each.")),a.validator.addMethod("maxsizetotal",function(b,c,d){if(this.optional(c))return!0;if("file"===a(c).attr("type")&&c.files&&c.files.length)for(var e=0,f=0;f<c.files.length;f++)if(e+=c.files[f].size,e>d)return!1;return!0},a.validator.format("Total size of all files must not exceed {0} bytes.")),a.validator.addMethod("mobileNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid mobile number"),a.validator.addMethod("mobileRU",function(a,b){var c=a.replace(/\(|\)|\s+|-/g,"");return this.optional(b)||c.length>9&&/^((\+7|7|8)+([0-9]){10})$/.test(c)},"Please specify a valid mobile number"),a.validator.addMethod("mobileUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number"),a.validator.addMethod("netmask",function(a,b){return this.optional(b)||/^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test(a)},"Please enter a valid netmask."),a.validator.addMethod("nieES",function(a,b){"use strict";if(this.optional(b))return!0;var c,d=new RegExp(/^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi),e="TRWAGMYFPDXBNJZSQVHLCKET",f=a.substr(a.length-1).toUpperCase();return a=a.toString().toUpperCase(),!(a.length>10||a.length<9||!d.test(a))&&(a=a.replace(/^[X]/,"0").replace(/^[Y]/,"1").replace(/^[Z]/,"2"),c=9===a.length?a.substr(0,8):a.substr(0,9),e.charAt(parseInt(c,10)%23)===f)},"Please specify a valid NIE number."),a.validator.addMethod("nifES",function(a,b){"use strict";return!!this.optional(b)||(a=a.toUpperCase(),!!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")&&(/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):!!/^[KLM]{1}/.test(a)&&a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,1)%23)))},"Please specify a valid NIF number."),a.validator.addMethod("nipPL",function(a){"use strict";if(a=a.replace(/[^0-9]/g,""),10!==a.length)return!1;for(var b=[6,5,7,2,3,4,5,6,7],c=0,d=0;d<9;d++)c+=b[d]*a[d];var e=c%11,f=10===e?0:e;return f===parseInt(a[9],10)},"Please specify a valid NIP number."),a.validator.addMethod("nisBR",function(a){var b,c,d,e,f,g=0;if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;for(c=parseInt(a.substring(10,11),10),b=parseInt(a.substring(0,10),10),e=2;e<12;e++)f=e,10===e&&(f=2),11===e&&(f=3),g+=b%10*f,b=parseInt(b/10,10);return d=g%11,d=d>1?11-d:0,c===d},"Please specify a valid NIS/PIS number"),a.validator.addMethod("notEqualTo",function(b,c,d){return this.optional(c)||!a.validator.methods.equalTo.call(this,b,c,d)},"Please enter a different value, values must not be the same."),a.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please"),a.validator.addMethod("pattern",function(a,b,c){return!!this.optional(b)||("string"==typeof c&&(c=new RegExp("^(?:"+c+")$")),c.test(a))},"Invalid format."),a.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonePL",function(a,b){a=a.replace(/\s+/g,"");var c=/^(?:(?:(?:\+|00)?48)|(?:\(\+?48\)))?(?:1[2-8]|2[2-69]|3[2-49]|4[1-68]|5[0-9]|6[0-35-9]|[7-8][1-9]|9[145])\d{7}$/;return this.optional(b)||c.test(a)},"Please specify a valid phone number"),a.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number"),a.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number"),a.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]\d{2}-?\d{4}$/)},"Please specify a valid phone number"),a.validator.addMethod("postalcodeBR",function(a,b){return this.optional(b)||/^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test(a)},"Informe um CEP válido."),a.validator.addMethod("postalCodeCA",function(a,b){return this.optional(b)||/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeIT",function(a,b){return this.optional(b)||/^\d{5}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode"),a.validator.addMethod("require_from_group",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_req_grp")?f.data("valid_req_grp"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length>=d[0];return f.data("valid_req_grp",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),h},a.validator.format("Please fill at least {0} of these fields.")),a.validator.addMethod("skip_or_fill_minimum",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_skip")?f.data("valid_skip"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length,i=0===h||h>=d[0];return f.data("valid_skip",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),i},a.validator.format("Please either skip these fields or fill at least {0} of them.")),a.validator.addMethod("stateUS",function(a,b,c){var d,e="undefined"==typeof c,f=!e&&"undefined"!=typeof c.caseSensitive&&c.caseSensitive,g=!e&&"undefined"!=typeof c.includeTerritories&&c.includeTerritories,h=!e&&"undefined"!=typeof c.includeMilitary&&c.includeMilitary;return d=g||h?g&&h?"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":g?"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$":"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$",d=f?new RegExp(d):new RegExp(d,"i"),this.optional(b)||d.test(a)},"Please specify a valid state"),a.validator.addMethod("strippedminlength",function(b,c,d){return a(b).text().length>=d},a.validator.format("Please enter at least {0} characters")),a.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59"),a.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format"),a.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},a.validator.messages.url),a.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;b<17;b++){if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c<h.length;c++)if(d.toUpperCase()===h[c]){d=i[c],d*=e,isNaN(g)&&8===c&&(g=h[c]);break}}else d*=e;k+=d}return f=k%11,10===f&&(f="X"),f===g},"The specified vehicle identification number (VIN) is invalid."),a.validator.addMethod("zipcodeUS",function(a,b){return this.optional(b)||/^\d{5}(-\d{4})?$/.test(a)},"The specified US ZIP Code is invalid"),a.validator.addMethod("ziprange",function(a,b){return this.optional(b)||/^90[2-5]\d\{2\}-\d{4}$/.test(a)},"Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx"),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.submitButton=b.currentTarget,a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.submitButton&&(c.settings.submitHandler||c.formSubmitted)&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!(c.settings.submitHandler&&!c.settings.debug)||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0],k="undefined"!=typeof this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=j&&(!j.form&&k&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}});var b=function(a){return a.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")};a.extend(a.expr.pseudos||a.expr[":"],{blank:function(c){return!b(""+a(c).val())},filled:function(c){var d=a(c).val();return null!==d&&!!b(""+d)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");if(!this.form&&c&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name")),d===this.form){var e=a.data(this.form,"validator"),f="on"+b.type.replace(/^validate/,""),g=e.settings;g[f]&&!a(this).is(g.ignore)&&g[f].call(e,this,b)}}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.currentForm,e=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){e[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").trigger("focus").trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name"),e="undefined"!=typeof a(this).attr("contenteditable")&&"false"!==a(this).attr("contenteditable");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),e&&(this.form=a(this).closest("form")[0],this.name=d),this.form===b.currentForm&&(!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0))})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type,g="undefined"!=typeof e.attr("contenteditable")&&"false"!==e.attr("contenteditable");return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=g?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);"function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f&&(j=f.call(b,j),delete g.normalizer);for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a]},defaultMessage:function(b,c){"string"==typeof c&&(c={method:c});var d=this.findDefined(this.customMessage(b.name,c.method),this.customDataMessage(b,c.method),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c.method],"<strong>Warning: No message defined for "+b.name+"</strong>"),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return a.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),""===d&&(d=!0),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(a,d){b[a]="function"==typeof d&&"normalizer"!==a?d(c):d}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var a;b[this]&&(Array.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(a=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(a[0]),Number(a[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:void 0!==b&&null!==b&&b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(a)},date:function(){var a=!1;return function(b,c){return a||(a=!0,this.settings.debug&&window.console&&console.warn("The `date` method is deprecated and will be removed in version '2.0.0'.\nPlease don't use it, since it relies on the Date constructor, which\nbehaves very differently across browsers and locales. Use `dateISO`\ninstead or one of the locale specific methods in `localizations/`\nand `additional-methods.js`.")),this.optional(c)||!/Invalid|NaN/.test(new Date(b).toString())}}(),dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c},maxlength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d<=c},rangelength:function(a,b,c){var d=Array.isArray(a)?a.length:this.getLength(a,b);return this.optional(b)||d>=c[0]&&d<=c[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var c,d={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,c){var e=a.port;"abort"===a.mode&&(d[e]&&d[e].abort(),d[e]=c)}):(c=a.ajax,a.ajax=function(b){var e=("mode"in b?b:a.ajaxSettings).mode,f=("port"in b?b:a.ajaxSettings).port;return"abort"===e?(d[f]&&d[f].abort(),d[f]=c.apply(this,arguments),d[f]):c.apply(this,arguments)}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"هذا الحقل إلزامي",remote:"يرجى تصحيح هذا الحقل للمتابعة",email:"رجاء إدخال عنوان بريد إلكتروني صحيح",url:"رجاء إدخال عنوان موقع إلكتروني صحيح",date:"رجاء إدخال تاريخ صحيح",dateISO:"رجاء إدخال تاريخ صحيح (ISO)",number:"رجاء إدخال عدد بطريقة صحيحة",digits:"رجاء إدخال أرقام فقط",creditcard:"رجاء إدخال رقم بطاقة ائتمان صحيح",equalTo:"رجاء إدخال نفس القيمة",extension:"رجاء إدخال ملف بامتداد موافق عليه",maxlength:a.validator.format("الحد الأقصى لعدد الحروف هو {0}"),minlength:a.validator.format("الحد الأدنى لعدد الحروف هو {0}"),rangelength:a.validator.format("عدد الحروف يجب أن يكون بين {0} و {1}"),range:a.validator.format("رجاء إدخال عدد قيمته بين {0} و {1}"),max:a.validator.format("رجاء إدخال عدد أقل من أو يساوي {0}"),min:a.validator.format("رجاء إدخال عدد أكبر من أو يساوي {0}")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Bu xana mütləq doldurulmalıdır.",remote:"Zəhmət olmasa, düzgün məna daxil edin.",email:"Zəhmət olmasa, düzgün elektron poçt daxil edin.",url:"Zəhmət olmasa, düzgün URL daxil edin.",date:"Zəhmət olmasa, düzgün tarix daxil edin.",dateISO:"Zəhmət olmasa, düzgün ISO formatlı tarix daxil edin.",number:"Zəhmət olmasa, düzgün rəqəm daxil edin.",digits:"Zəhmət olmasa, yalnız rəqəm daxil edin.",creditcard:"Zəhmət olmasa, düzgün kredit kart nömrəsini daxil edin.",equalTo:"Zəhmət olmasa, eyni mənanı bir daha daxil edin.",extension:"Zəhmət olmasa, düzgün genişlənməyə malik faylı seçin.",maxlength:a.validator.format("Zəhmət olmasa, {0} simvoldan çox olmayaraq daxil edin."),minlength:a.validator.format("Zəhmət olmasa, {0} simvoldan az olmayaraq daxil edin."),rangelength:a.validator.format("Zəhmət olmasa, {0} - {1} aralığında uzunluğa malik simvol daxil edin."),range:a.validator.format("Zəhmət olmasa, {0} - {1} aralığında rəqəm daxil edin."),max:a.validator.format("Zəhmət olmasa, {0} və ondan kiçik rəqəm daxil edin."),min:a.validator.format("Zəhmət olmasa, {0} və ondan böyük rəqəm daxil edin")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Полето е задължително.",remote:"Моля, въведете правилната стойност.",email:"Моля, въведете валиден email.",url:"Моля, въведете валидно URL.",date:"Моля, въведете валидна дата.",dateISO:"Моля, въведете валидна дата (ISO).",number:"Моля, въведете валиден номер.",digits:"Моля, въведете само цифри.",creditcard:"Моля, въведете валиден номер на кредитна карта.",equalTo:"Моля, въведете същата стойност отново.",extension:"Моля, въведете стойност с валидно разширение.",maxlength:a.validator.format("Моля, въведете не повече от {0} символа."),minlength:a.validator.format("Моля, въведете поне {0} символа."),rangelength:a.validator.format("Моля, въведете стойност с дължина между {0} и {1} символа."),range:a.validator.format("Моля, въведете стойност между {0} и {1}."),max:a.validator.format("Моля, въведете стойност по-малка или равна на {0}."),min:a.validator.format("Моля, въведете стойност по-голяма или равна на {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"এই তথ্যটি আবশ্যক।",remote:"এই তথ্যটি ঠিক করুন।",email:"অনুগ্রহ করে একটি সঠিক মেইল ঠিকানা লিখুন।",url:"অনুগ্রহ করে একটি সঠিক লিঙ্ক দিন।",date:"তারিখ সঠিক নয়।",dateISO:"অনুগ্রহ করে একটি সঠিক (ISO) তারিখ লিখুন।",number:"অনুগ্রহ করে একটি সঠিক নম্বর লিখুন।",digits:"এখানে শুধু সংখ্যা ব্যবহার করা যাবে।",creditcard:"অনুগ্রহ করে একটি ক্রেডিট কার্ডের সঠিক নম্বর লিখুন।",equalTo:"একই মান আবার লিখুন।",extension:"সঠিক ধরনের ফাইল আপলোড করুন।",maxlength:a.validator.format("{0}টির বেশি অক্ষর লেখা যাবে না।"),minlength:a.validator.format("{0}টির কম অক্ষর লেখা যাবে না।"),rangelength:a.validator.format("{0} থেকে {1} টি অক্ষর সম্বলিত মান লিখুন।"),range:a.validator.format("{0} থেকে {1} এর মধ্যে একটি মান ব্যবহার করুন।"),max:a.validator.format("অনুগ্রহ করে {0} বা তার চাইতে কম মান ব্যবহার করুন।"),min:a.validator.format("অনুগ্রহ করে {0} বা তার চাইতে বেশি মান ব্যবহার করুন।")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Aquest camp és obligatori.",remote:"Si us plau, omple aquest camp.",email:"Si us plau, escriu una adreça de correu-e vàlida",url:"Si us plau, escriu una URL vàlida.",date:"Si us plau, escriu una data vàlida.",dateISO:"Si us plau, escriu una data (ISO) vàlida.",number:"Si us plau, escriu un número enter vàlid.",digits:"Si us plau, escriu només dígits.",creditcard:"Si us plau, escriu un número de tarjeta vàlid.",equalTo:"Si us plau, escriu el mateix valor de nou.",extension:"Si us plau, escriu un valor amb una extensió acceptada.",maxlength:a.validator.format("Si us plau, no escriguis més de {0} caracters."),minlength:a.validator.format("Si us plau, no escriguis menys de {0} caracters."),rangelength:a.validator.format("Si us plau, escriu un valor entre {0} i {1} caracters."),range:a.validator.format("Si us plau, escriu un valor entre {0} i {1}."),max:a.validator.format("Si us plau, escriu un valor menor o igual a {0}."),min:a.validator.format("Si us plau, escriu un valor major o igual a {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Tento údaj je povinný.",remote:"Prosím, opravte tento údaj.",email:"Prosím, zadejte platný e-mail.",url:"Prosím, zadejte platné URL.",date:"Prosím, zadejte platné datum.",dateISO:"Prosím, zadejte platné datum (ISO).",number:"Prosím, zadejte číslo.",digits:"Prosím, zadávejte pouze číslice.",creditcard:"Prosím, zadejte číslo kreditní karty.",equalTo:"Prosím, zadejte znovu stejnou hodnotu.",extension:"Prosím, zadejte soubor se správnou příponou.",maxlength:a.validator.format("Prosím, zadejte nejvíce {0} znaků."),minlength:a.validator.format("Prosím, zadejte nejméně {0} znaků."),rangelength:a.validator.format("Prosím, zadejte od {0} do {1} znaků."),range:a.validator.format("Prosím, zadejte hodnotu od {0} do {1}."),max:a.validator.format("Prosím, zadejte hodnotu menší nebo rovnu {0}."),min:a.validator.format("Prosím, zadejte hodnotu větší nebo rovnu {0}."),step:a.validator.format("Musí být násobkem čísla {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Dette felt er påkrævet.",remote:"Ret venligst dette felt",email:"Indtast en gyldig email-adresse.",url:"Indtast en gyldig URL.",date:"Indtast en gyldig dato.",number:"Indtast et tal.",digits:"Indtast kun cifre.",creditcard:"Indtast et gyldigt kreditkortnummer.",equalTo:"Indtast den samme værdi igen.",time:"Angiv en gyldig tid mellem kl. 00:00 og 23:59.",ipv4:"Angiv venligst en gyldig IPv4-adresse.",ipv6:"Angiv venligst en gyldig IPv6-adresse.",require_from_group:a.validator.format("Angiv mindst {0} af disse felter."),extension:"Indtast venligst en værdi med en gyldig endelse",pattern:"Ugyldigt format",lettersonly:"Angiv venligst kun bogstaver.",nowhitespace:"Må ikke indholde mellemrum",maxlength:a.validator.format("Indtast højst {0} tegn."),minlength:a.validator.format("Indtast mindst {0} tegn."),rangelength:a.validator.format("Indtast mindst {0} og højst {1} tegn."),range:a.validator.format("Angiv en værdi mellem {0} og {1}."),max:a.validator.format("Angiv en værdi der højst er {0}."),min:a.validator.format("Angiv en værdi der mindst er {0}."),minWords:a.validator.format("Indtast venligst mindst {0} ord"),maxWords:a.validator.format("Indtast venligst højst {0} ord"),step:a.validator.format("Angiv en værdi gange {0}."),notEqualTo:"Angiv en anden værdi, værdierne må ikke være det samme.",integer:"Angiv et ikke-decimaltal, der er positivt eller negativt."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Dieses Feld ist ein Pflichtfeld.",maxlength:a.validator.format("Geben Sie bitte maximal {0} Zeichen ein."),minlength:a.validator.format("Geben Sie bitte mindestens {0} Zeichen ein."),rangelength:a.validator.format("Geben Sie bitte mindestens {0} und maximal {1} Zeichen ein."),email:"Geben Sie bitte eine gültige E-Mail-Adresse ein.",url:"Geben Sie bitte eine gültige URL ein.",date:"Geben Sie bitte ein gültiges Datum ein.",number:"Geben Sie bitte eine Nummer ein.",digits:"Geben Sie bitte nur Ziffern ein.",equalTo:"Wiederholen Sie bitte denselben Wert.",range:a.validator.format("Geben Sie bitte einen Wert zwischen {0} und {1} ein."),max:a.validator.format("Geben Sie bitte einen Wert kleiner oder gleich {0} ein."),min:a.validator.format("Geben Sie bitte einen Wert größer oder gleich {0} ein."),creditcard:"Geben Sie bitte eine gültige Kreditkarten-Nummer ein.",remote:"Korrigieren Sie bitte dieses Feld.",dateISO:"Geben Sie bitte ein gültiges Datum ein (ISO-Format).",step:a.validator.format("Geben Sie bitte ein Vielfaches von {0} ein."),maxWords:a.validator.format("Geben Sie bitte {0} Wörter oder weniger ein."),minWords:a.validator.format("Geben Sie bitte mindestens {0} Wörter ein."),rangeWords:a.validator.format("Geben Sie bitte zwischen {0} und {1} Wörtern ein."),accept:"Geben Sie bitte einen Wert mit einem gültigen MIME-Typ ein.",alphanumeric:"Geben Sie bitte nur Buchstaben (keine Umlaute), Zahlen oder Unterstriche ein.",bankaccountNL:"Geben Sie bitte eine gültige Kontonummer ein.",bankorgiroaccountNL:"Geben Sie bitte eine gültige Bank- oder Girokontonummer ein.",bic:"Geben Sie bitte einen gültigen BIC-Code ein.",cifES:"Geben Sie bitte eine gültige CIF-Nummer ein.",cpfBR:"Geben Sie bitte eine gültige CPF-Nummer ein.",creditcardtypes:"Geben Sie bitte eine gültige Kreditkarten-Nummer ein.",currency:"Geben Sie bitte eine gültige Währung ein.",extension:"Geben Sie bitte einen Wert mit einer gültigen Erweiterung ein.",giroaccountNL:"Geben Sie bitte eine gültige Girokontonummer ein.",iban:"Geben Sie bitte eine gültige IBAN ein.",integer:"Geben Sie bitte eine positive oder negative Nicht-Dezimalzahl ein.",ipv4:"Geben Sie bitte eine gültige IPv4-Adresse ein.",ipv6:"Geben Sie bitte eine gültige IPv6-Adresse ein.",lettersonly:"Geben Sie bitte nur Buchstaben ein.",letterswithbasicpunc:"Geben Sie bitte nur Buchstaben oder Interpunktion ein.",mobileNL:"Geben Sie bitte eine gültige Handynummer ein.",mobileUK:"Geben Sie bitte eine gültige Handynummer ein.",netmask:"Geben Sie bitte eine gültige Netzmaske ein.",nieES:"Geben Sie bitte eine gültige NIE-Nummer ein.",nifES:"Geben Sie bitte eine gültige NIF-Nummer ein.",nipPL:"Geben Sie bitte eine gültige NIP-Nummer ein.",notEqualTo:"Geben Sie bitte einen anderen Wert ein. Die Werte dürfen nicht gleich sein.",nowhitespace:"Kein Leerzeichen bitte.",pattern:"Ungültiges Format.",phoneNL:"Geben Sie bitte eine gültige Telefonnummer ein.",phonesUK:"Geben Sie bitte eine gültige britische Telefonnummer ein.",phoneUK:"Geben Sie bitte eine gültige Telefonnummer ein.",phoneUS:"Geben Sie bitte eine gültige Telefonnummer ein.",postalcodeBR:"Geben Sie bitte eine gültige brasilianische Postleitzahl ein.",postalCodeCA:"Geben Sie bitte eine gültige kanadische Postleitzahl ein.",postalcodeIT:"Geben Sie bitte eine gültige italienische Postleitzahl ein.",postalcodeNL:"Geben Sie bitte eine gültige niederländische Postleitzahl ein.",postcodeUK:"Geben Sie bitte eine gültige britische Postleitzahl ein.",require_from_group:a.validator.format("Füllen Sie bitte mindestens {0} dieser Felder aus."),skip_or_fill_minimum:a.validator.format("Überspringen Sie bitte diese Felder oder füllen Sie mindestens {0} von ihnen aus."),stateUS:"Geben Sie bitte einen gültigen US-Bundesstaat ein.",strippedminlength:a.validator.format("Geben Sie bitte mindestens {0} Zeichen ein."),time:"Geben Sie bitte eine gültige Uhrzeit zwischen 00:00 und 23:59 ein.",time12h:"Geben Sie bitte eine gültige Uhrzeit im 12-Stunden-Format ein.",vinUS:"Die angegebene Fahrzeugidentifikationsnummer (VIN) ist ungültig.",zipcodeUS:"Die angegebene US-Postleitzahl ist ungültig.",ziprange:"Ihre Postleitzahl muss im Bereich 902xx-xxxx bis 905xx-xxxx liegen."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Αυτό το πεδίο είναι υποχρεωτικό.",remote:"Παρακαλώ διορθώστε αυτό το πεδίο.",email:"Παρακαλώ εισάγετε μια έγκυρη διεύθυνση email.",url:"Παρακαλώ εισάγετε ένα έγκυρο URL.",date:"Παρακαλώ εισάγετε μια έγκυρη ημερομηνία.",dateISO:"Παρακαλώ εισάγετε μια έγκυρη ημερομηνία (ISO).",number:"Παρακαλώ εισάγετε έναν έγκυρο αριθμό.",digits:"Παρακαλώ εισάγετε μόνο αριθμητικά ψηφία.",creditcard:"Παρακαλώ εισάγετε έναν έγκυρο αριθμό πιστωτικής κάρτας.",equalTo:"Παρακαλώ εισάγετε την ίδια τιμή ξανά.",extension:"Παρακαλώ εισάγετε μια τιμή με έγκυρη επέκταση αρχείου.",maxlength:a.validator.format("Παρακαλώ εισάγετε μέχρι και {0} χαρακτήρες."),minlength:a.validator.format("Παρακαλώ εισάγετε τουλάχιστον {0} χαρακτήρες."),rangelength:a.validator.format("Παρακαλώ εισάγετε μια τιμή με μήκος μεταξύ {0} και {1} χαρακτήρων."),range:a.validator.format("Παρακαλώ εισάγετε μια τιμή μεταξύ {0} και {1}."),max:a.validator.format("Παρακαλώ εισάγετε μια τιμή μικρότερη ή ίση του {0}."),min:a.validator.format("Παρακαλώ εισάγετε μια τιμή μεγαλύτερη ή ίση του {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Este campo es obligatorio.",remote:"Por favor, rellena este campo.",email:"Por favor, escribe una dirección de correo válida.",url:"Por favor, escribe una URL válida.",date:"Por favor, escribe una fecha válida.",dateISO:"Por favor, escribe una fecha (ISO) válida.",number:"Por favor, escribe un número válido.",digits:"Por favor, escribe sólo dígitos.",creditcard:"Por favor, escribe un número de tarjeta válido.",equalTo:"Por favor, escribe el mismo valor de nuevo.",extension:"Por favor, escribe un valor con una extensión aceptada.",maxlength:a.validator.format("Por favor, no escribas más de {0} caracteres."),minlength:a.validator.format("Por favor, no escribas menos de {0} caracteres."),rangelength:a.validator.format("Por favor, escribe un valor entre {0} y {1} caracteres."),range:a.validator.format("Por favor, escribe un valor entre {0} y {1}."),max:a.validator.format("Por favor, escribe un valor menor o igual a {0}."),min:a.validator.format("Por favor, escribe un valor mayor o igual a {0}."),nifES:"Por favor, escribe un NIF válido.",nieES:"Por favor, escribe un NIE válido.",cifES:"Por favor, escribe un CIF válido."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Este campo es obligatorio.",remote:"Por favor, completá este campo.",email:"Por favor, escribí una dirección de correo válida.",url:"Por favor, escribí una URL válida.",date:"Por favor, escribí una fecha válida.",dateISO:"Por favor, escribí una fecha (ISO) válida.",number:"Por favor, escribí un número entero válido.",digits:"Por favor, escribí sólo dígitos.",creditcard:"Por favor, escribí un número de tarjeta válido.",equalTo:"Por favor, escribí el mismo valor de nuevo.",extension:"Por favor, escribí un valor con una extensión aceptada.",maxlength:a.validator.format("Por favor, no escribas más de {0} caracteres."),minlength:a.validator.format("Por favor, no escribas menos de {0} caracteres."),rangelength:a.validator.format("Por favor, escribí un valor entre {0} y {1} caracteres."),range:a.validator.format("Por favor, escribí un valor entre {0} y {1}."),max:a.validator.format("Por favor, escribí un valor menor o igual a {0}."),min:a.validator.format("Por favor, escribí un valor mayor o igual a {0}."),nifES:"Por favor, escribí un NIF válido.",nieES:"Por favor, escribí un NIE válido.",cifES:"Por favor, escribí un CIF válido."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Este campo es obligatorio.",remote:"Por favor, llene este campo.",email:"Por favor, escriba un correo electrónico válido.",url:"Por favor, escriba una URL válida.",date:"Por favor, escriba una fecha válida.",dateISO:"Por favor, escriba una fecha (ISO) válida.",number:"Por favor, escriba un número válido.",digits:"Por favor, escriba sólo dígitos.",creditcard:"Por favor, escriba un número de tarjeta válido.",equalTo:"Por favor, escriba el mismo valor de nuevo.",extension:"Por favor, escriba un valor con una extensión permitida.",maxlength:a.validator.format("Por favor, no escriba más de {0} caracteres."),minlength:a.validator.format("Por favor, no escriba menos de {0} caracteres."),rangelength:a.validator.format("Por favor, escriba un valor entre {0} y {1} caracteres."),range:a.validator.format("Por favor, escriba un valor entre {0} y {1}."),max:a.validator.format("Por favor, escriba un valor menor o igual a {0}."),min:a.validator.format("Por favor, escriba un valor mayor o igual a {0}."),nifES:"Por favor, escriba un NIF válido.",nieES:"Por favor, escriba un NIE válido.",cifES:"Por favor, escriba un CIF válido."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"See väli peab olema täidetud.",maxlength:a.validator.format("Palun sisestage vähem kui {0} tähemärki."),minlength:a.validator.format("Palun sisestage vähemalt {0} tähemärki."),rangelength:a.validator.format("Palun sisestage väärtus vahemikus {0} kuni {1} tähemärki."),email:"Palun sisestage korrektne e-maili aadress.",url:"Palun sisestage korrektne URL.",date:"Palun sisestage korrektne kuupäev.",dateISO:"Palun sisestage korrektne kuupäev (YYYY-MM-DD).",number:"Palun sisestage korrektne number.",digits:"Palun sisestage ainult numbreid.",equalTo:"Palun sisestage sama väärtus uuesti.",range:a.validator.format("Palun sisestage väärtus vahemikus {0} kuni {1}."),max:a.validator.format("Palun sisestage väärtus, mis on väiksem või võrdne arvuga {0}."),min:a.validator.format("Palun sisestage väärtus, mis on suurem või võrdne arvuga {0}."),creditcard:"Palun sisestage korrektne krediitkaardi number."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Eremu hau beharrezkoa da.",remote:"Mesedez, bete eremu hau.",email:"Mesedez, idatzi baliozko posta helbide bat.",url:"Mesedez, idatzi baliozko URL bat.",date:"Mesedez, idatzi baliozko data bat.",dateISO:"Mesedez, idatzi baliozko (ISO) data bat.",number:"Mesedez, idatzi baliozko zenbaki oso bat.",digits:"Mesedez, idatzi digituak soilik.",creditcard:"Mesedez, idatzi baliozko txartel zenbaki bat.",equalTo:"Mesedez, idatzi berdina berriro ere.",extension:"Mesedez, idatzi onartutako luzapena duen balio bat.",maxlength:a.validator.format("Mesedez, ez idatzi {0} karaktere baino gehiago."),minlength:a.validator.format("Mesedez, ez idatzi {0} karaktere baino gutxiago."),rangelength:a.validator.format("Mesedez, idatzi {0} eta {1} karaktere arteko balio bat."),range:a.validator.format("Mesedez, idatzi {0} eta {1} arteko balio bat."),max:a.validator.format("Mesedez, idatzi {0} edo txikiagoa den balio bat."),min:a.validator.format("Mesedez, idatzi {0} edo handiagoa den balio bat.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"تکمیل این فیلد اجباری است.",remote:"لطفا این فیلد را تصحیح کنید.",email:"لطفا یک ایمیل صحیح وارد کنید.",url:"لطفا آدرس صحیح وارد کنید.",date:"لطفا تاریخ صحیح وارد کنید.",dateFA:"لطفا یک تاریخ صحیح وارد کنید.",dateISO:"لطفا تاریخ صحیح وارد کنید (ISO).",number:"لطفا عدد صحیح وارد کنید.",digits:"لطفا تنها رقم وارد کنید.",creditcard:"لطفا کریدیت کارت صحیح وارد کنید.",equalTo:"لطفا مقدار برابری وارد کنید.",extension:"لطفا مقداری وارد کنید که",alphanumeric:"لطفا مقدار را عدد (انگلیسی) وارد کنید.",maxlength:a.validator.format("لطفا بیشتر از {0} حرف وارد نکنید."),minlength:a.validator.format("لطفا کمتر از {0} حرف وارد نکنید."),rangelength:a.validator.format("لطفا مقداری بین {0} تا {1} حرف وارد کنید."),range:a.validator.format("لطفا مقداری بین {0} تا {1} حرف وارد کنید."),max:a.validator.format("لطفا مقداری کمتر از {0} وارد کنید."),min:a.validator.format("لطفا مقداری بیشتر از {0} وارد کنید."),minWords:a.validator.format("لطفا حداقل {0} کلمه وارد کنید."),maxWords:a.validator.format("لطفا حداکثر {0} کلمه وارد کنید.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"T&auml;m&auml; kentt&auml; on pakollinen.",email:"Sy&ouml;t&auml; oikea s&auml;hk&ouml;postiosoite.",url:"Sy&ouml;t&auml; oikea URL-osoite.",date:"Sy&ouml;t&auml; oikea p&auml;iv&auml;m&auml;&auml;r&auml;.",dateISO:"Sy&ouml;t&auml; oikea p&auml;iv&auml;m&auml;&auml;r&auml; muodossa VVVV-KK-PP.",number:"Sy&ouml;t&auml; luku.",creditcard:"Sy&ouml;t&auml; voimassa oleva luottokorttinumero.",digits:"Sy&ouml;t&auml; pelk&auml;st&auml;&auml;n numeroita.",equalTo:"Sy&ouml;t&auml; sama arvo uudestaan.",maxlength:a.validator.format("Voit sy&ouml;tt&auml;&auml; enint&auml;&auml;n {0} merkki&auml;."),minlength:a.validator.format("V&auml;hint&auml;&auml;n {0} merkki&auml;."),rangelength:a.validator.format("Sy&ouml;t&auml; v&auml;hint&auml;&auml;n {0} ja enint&auml;&auml;n {1} merkki&auml;."),range:a.validator.format("Sy&ouml;t&auml; arvo v&auml;lilt&auml; {0}&ndash;{1}."),max:a.validator.format("Sy&ouml;t&auml; arvo, joka on enint&auml;&auml;n {0}."),min:a.validator.format("Sy&ouml;t&auml; arvo, joka on v&auml;hint&auml;&auml;n {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Ce champ est obligatoire.",remote:"Veuillez corriger ce champ.",email:"Veuillez fournir une adresse électronique valide.",url:"Veuillez fournir une adresse URL valide.",date:"Veuillez fournir une date valide.",dateISO:"Veuillez fournir une date valide (ISO).",number:"Veuillez fournir un numéro valide.",digits:"Veuillez fournir seulement des chiffres.",creditcard:"Veuillez fournir un numéro de carte de crédit valide.",equalTo:"Veuillez fournir encore la même valeur.",notEqualTo:"Veuillez fournir une valeur différente, les valeurs ne doivent pas être identiques.",extension:"Veuillez fournir une valeur avec une extension valide.",maxlength:a.validator.format("Veuillez fournir au plus {0} caractères."),minlength:a.validator.format("Veuillez fournir au moins {0} caractères."),rangelength:a.validator.format("Veuillez fournir une valeur qui contient entre {0} et {1} caractères."),range:a.validator.format("Veuillez fournir une valeur entre {0} et {1}."),max:a.validator.format("Veuillez fournir une valeur inférieure ou égale à {0}."),min:a.validator.format("Veuillez fournir une valeur supérieure ou égale à {0}."),step:a.validator.format("Veuillez fournir une valeur multiple de {0}."),maxWords:a.validator.format("Veuillez fournir au plus {0} mots."),minWords:a.validator.format("Veuillez fournir au moins {0} mots."),rangeWords:a.validator.format("Veuillez fournir entre {0} et {1} mots."),letterswithbasicpunc:"Veuillez fournir seulement des lettres et des signes de ponctuation.",alphanumeric:"Veuillez fournir seulement des lettres, nombres, espaces et soulignages.",lettersonly:"Veuillez fournir seulement des lettres.",nowhitespace:"Veuillez ne pas inscrire d'espaces blancs.",ziprange:"Veuillez fournir un code postal entre 902xx-xxxx et 905-xx-xxxx.",integer:"Veuillez fournir un nombre non décimal qui est positif ou négatif.",vinUS:"Veuillez fournir un numéro d'identification du véhicule (VIN).",dateITA:"Veuillez fournir une date valide.",time:"Veuillez fournir une heure valide entre 00:00 et 23:59.",phoneUS:"Veuillez fournir un numéro de téléphone valide.",phoneUK:"Veuillez fournir un numéro de téléphone valide.",mobileUK:"Veuillez fournir un numéro de téléphone mobile valide.",strippedminlength:a.validator.format("Veuillez fournir au moins {0} caractères."),email2:"Veuillez fournir une adresse électronique valide.",url2:"Veuillez fournir une adresse URL valide.",creditcardtypes:"Veuillez fournir un numéro de carte de crédit valide.",ipv4:"Veuillez fournir une adresse IP v4 valide.",ipv6:"Veuillez fournir une adresse IP v6 valide.",require_from_group:a.validator.format("Veuillez fournir au moins {0} de ces champs."),nifES:"Veuillez fournir un numéro NIF valide.",nieES:"Veuillez fournir un numéro NIE valide.",cifES:"Veuillez fournir un numéro CIF valide.",postalCodeCA:"Veuillez fournir un code postal valide.",pattern:"Format non valide."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"ეს ველი სავალდებულოა",remote:"გთხოვთ შეასწოროთ.",email:"გთხოვთ შეიყვანოთ სწორი ფორმატით.",url:"გთხოვთ შეიყვანოთ სწორი ფორმატით.",date:"გთხოვთ შეიყვანოთ სწორი თარიღი.",dateISO:"გთხოვთ შეიყვანოთ სწორი ფორმატით (ISO).",number:"გთხოვთ შეიყვანოთ რიცხვი.",digits:"დაშვებულია მხოლოდ ციფრები.",creditcard:"გთხოვთ შეიყვანოთ სწორი ფორმატის ბარათის კოდი.",equalTo:"გთხოვთ შეიყვანოთ იგივე მნიშვნელობა.",maxlength:a.validator.format("გთხოვთ შეიყვანოთ არა უმეტეს {0} სიმბოლოსი."),minlength:a.validator.format("შეიყვანეთ მინიმუმ {0} სიმბოლო."),rangelength:a.validator.format("გთხოვთ შეიყვანოთ {0} -დან {1} -მდე რაოდენობის სიმბოლოები."),range:a.validator.format("შეიყვანეთ {0} -სა {1} -ს შორის."),max:a.validator.format("გთხოვთ შეიყვანოთ მნიშვნელობა ნაკლები ან ტოლი {0} -ს."),min:a.validator.format("გთხოვთ შეიყვანოთ მნიშვნელობა მეტი ან ტოლი {0} -ს.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(a){a.extend(a.validator.messages,{required:"Este campo é obrigatorio.",remote:"Por favor, cubre este campo.",email:"Por favor, escribe unha dirección de correo válida.",url:"Por favor, escribe unha URL válida.",date:"Por favor, escribe unha data válida.",dateISO:"Por favor, escribe unha data (ISO) válida.",number:"Por favor, escribe un número válido.",digits:"Por favor, escribe só díxitos.",creditcard:"Por favor, escribe un número de tarxeta válido.",equalTo:"Por favor, escribe o mesmo valor de novo.",extension:"Por favor, escribe un valor cunha extensión aceptada.",maxlength:a.validator.format("Por favor, non escribas máis de {0} caracteres."),minlength:a.validator.format("Por favor, non escribas menos de {0} caracteres."),rangelength:a.validator.format("Por favor, escribe un valor entre {0} e {1} caracteres."),range:a.validator.format("Por favor, escribe un valor entre {0} e {1}."),max:a.validator.format("Por favor, escribe un valor menor ou igual a {0}."),min:a.validator.format("Por favor, escribe un valor maior ou igual a {0}."),nifES:"Por favor, escribe un NIF válido.",nieES:"Por favor, escribe un NIE válido.",cifES:"Por favor, escribe un CIF válido."})}(jQuery),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"השדה הזה הינו שדה חובה",remote:"נא לתקן שדה זה",email:'נא למלא כתובת דוא"ל חוקית',url:"נא למלא כתובת אינטרנט חוקית",date:"נא למלא תאריך חוקי",dateISO:"נא למלא תאריך חוקי (ISO)",number:"נא למלא מספר",digits:"נא למלא רק מספרים",creditcard:"נא למלא מספר כרטיס אשראי חוקי",equalTo:"נא למלא את אותו ערך שוב",extension:"נא למלא ערך עם סיומת חוקית",maxlength:a.validator.format(".נא לא למלא יותר מ- {0} תווים"),minlength:a.validator.format("נא למלא לפחות {0} תווים"),rangelength:a.validator.format("נא למלא ערך בין {0} ל- {1} תווים"),range:a.validator.format("נא למלא ערך בין {0} ל- {1}"),max:a.validator.format("נא למלא ערך קטן או שווה ל- {0}"),min:a.validator.format("נא למלא ערך גדול או שווה ל- {0}")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Ovo polje je obavezno.",remote:"Ovo polje treba popraviti.",email:"Unesite ispravnu e-mail adresu.",url:"Unesite ispravan URL.",date:"Unesite ispravan datum.",dateISO:"Unesite ispravan datum (ISO).",number:"Unesite ispravan broj.",digits:"Unesite samo brojeve.",creditcard:"Unesite ispravan broj kreditne kartice.",equalTo:"Unesite ponovo istu vrijednost.",extension:"Unesite vrijednost sa ispravnom ekstenzijom.",maxlength:a.validator.format("Maksimalni broj znakova je {0} ."),minlength:a.validator.format("Minimalni broj znakova je {0} ."),rangelength:a.validator.format("Unesite vrijednost između {0} i {1} znakova."),range:a.validator.format("Unesite vrijednost između {0} i {1}."),max:a.validator.format("Unesite vrijednost manju ili jednaku {0}."),min:a.validator.format("Unesite vrijednost veću ili jednaku {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Kötelező megadni.",maxlength:a.validator.format("Legfeljebb {0} karakter hosszú legyen."),minlength:a.validator.format("Legalább {0} karakter hosszú legyen."),rangelength:a.validator.format("Legalább {0} és legfeljebb {1} karakter hosszú legyen."),email:"Érvényes e-mail címnek kell lennie.",url:"Érvényes URL-nek kell lennie.",date:"Dátumnak kell lennie.",number:"Számnak kell lennie.",digits:"Csak számjegyek lehetnek.",equalTo:"Meg kell egyeznie a két értéknek.",range:a.validator.format("{0} és {1} közé kell esnie."),max:a.validator.format("Nem lehet nagyobb, mint {0}."),min:a.validator.format("Nem lehet kisebb, mint {0}."),creditcard:"Érvényes hitelkártyaszámnak kell lennie.",remote:"Kérem javítsa ki ezt a mezőt.",dateISO:"Kérem írjon be egy érvényes dátumot (ISO).",step:a.validator.format("A {0} egyik többszörösét adja meg.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Պարտադիր լրացման դաշտ",remote:"Ներմուծեք ճիշտ արժեքը",email:"Ներմուծեք վավեր էլեկտրոնային փոստի հասցե",url:"Ներմուծեք վավեր URL",date:"Ներմուծեք վավեր ամսաթիվ",dateISO:"Ներմուծեք ISO ֆորմատով վավեր ամսաթիվ։",number:"Ներմուծեք թիվ",digits:"Ներմուծեք միայն թվեր",creditcard:"Ներմուծեք ճիշտ բանկային քարտի համար",equalTo:"Ներմուծեք միևնուն արժեքը ևս մեկ անգամ",extension:"Ընտրեք ճիշտ ընդլանումով ֆայլ",maxlength:a.validator.format("Ներմուծեք ոչ ավել քան {0} նիշ"),minlength:a.validator.format("Ներմուծեք ոչ պակաս քան {0} նիշ"),rangelength:a.validator.format("Ներմուծեք {0}֊ից {1} երկարությամբ արժեք"),range:a.validator.format("Ներմուծեք թիվ {0}֊ից {1} միջակայքում"),max:a.validator.format("Ներմուծեք թիվ, որը փոքր կամ հավասար է {0}֊ին"),min:a.validator.format("Ներմուծեք թիվ, որը մեծ կամ հավասար է {0}֊ին")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Kolom ini diperlukan.",remote:"Harap benarkan kolom ini.",email:"Silakan masukkan format email yang benar.",url:"Silakan masukkan format URL yang benar.",date:"Silakan masukkan format tanggal yang benar.",dateISO:"Silakan masukkan format tanggal(ISO) yang benar.",number:"Silakan masukkan angka yang benar.",digits:"Harap masukan angka saja.",creditcard:"Harap masukkan format kartu kredit yang benar.",equalTo:"Harap masukkan nilai yg sama dengan sebelumnya.",maxlength:a.validator.format("Input dibatasi hanya {0} karakter."),minlength:a.validator.format("Input tidak kurang dari {0} karakter."),rangelength:a.validator.format("Panjang karakter yg diizinkan antara {0} dan {1} karakter."),range:a.validator.format("Harap masukkan nilai antara {0} dan {1}."),max:a.validator.format("Harap masukkan nilai lebih kecil atau sama dengan {0}."),min:a.validator.format("Harap masukkan nilai lebih besar atau sama dengan {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Þessi reitur er nauðsynlegur.",remote:"Lagaðu þennan reit.",maxlength:a.validator.format("Sláðu inn mest {0} stafi."),minlength:a.validator.format("Sláðu inn minnst {0} stafi."),rangelength:a.validator.format("Sláðu inn minnst {0} og mest {1} stafi."),email:"Sláðu inn gilt netfang.",url:"Sláðu inn gilda vefslóð.",date:"Sláðu inn gilda dagsetningu.",number:"Sláðu inn tölu.",digits:"Sláðu inn tölustafi eingöngu.",equalTo:"Sláðu sama gildi inn aftur.",range:a.validator.format("Sláðu inn gildi milli {0} og {1}."),max:a.validator.format("Sláðu inn gildi sem er minna en eða jafnt og {0}."),min:a.validator.format("Sláðu inn gildi sem er stærra en eða jafnt og {0}."),creditcard:"Sláðu inn gilt greiðslukortanúmer."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Campo obbligatorio",remote:"Controlla questo campo",email:"Inserisci un indirizzo email valido",url:"Inserisci un indirizzo web valido",date:"Inserisci una data valida",dateISO:"Inserisci una data valida (ISO)",number:"Inserisci un numero valido",digits:"Inserisci solo numeri",creditcard:"Inserisci un numero di carta di credito valido",equalTo:"Il valore non corrisponde",extension:"Inserisci un valore con un&apos;estensione valida",maxlength:a.validator.format("Non inserire pi&ugrave; di {0} caratteri"),minlength:a.validator.format("Inserisci almeno {0} caratteri"),rangelength:a.validator.format("Inserisci un valore compreso tra {0} e {1} caratteri"),range:a.validator.format("Inserisci un valore compreso tra {0} e {1}"),max:a.validator.format("Inserisci un valore minore o uguale a {0}"),min:a.validator.format("Inserisci un valore maggiore o uguale a {0}"),nifES:"Inserisci un NIF valido",nieES:"Inserisci un NIE valido",cifES:"Inserisci un CIF valido",currency:"Inserisci una valuta valida"}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"このフィールドは必須です。",remote:"このフィールドを修正してください。",email:"有効なEメールアドレスを入力してください。",url:"有効なURLを入力してください。",date:"有効な日付を入力してください。",dateISO:"有効な日付ISOを入力してください。",number:"有効な数字を入力してください。",digits:"数字のみを入力してください。",creditcard:"有効なクレジットカード番号を入力してください。",equalTo:"同じ値をもう一度入力してください。",extension:"有効な拡張子を含む値を入力してください。",maxlength:a.validator.format("{0} 文字以内で入力してください。"),minlength:a.validator.format("{0} 文字以上で入力してください。"),rangelength:a.validator.format("{0} 文字から {1} 文字までの値を入力してください。"),range:a.validator.format("{0} から {1} までの値を入力してください。"),step:a.validator.format("{0} の倍数を入力してください。"),max:a.validator.format("{0} 以下の値を入力してください。"),min:a.validator.format("{0} 以上の値を入力してください。")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"ამ ველის შევსება აუცილებელია.",remote:"გთხოვთ მიუთითოთ სწორი მნიშვნელობა.",email:"გთხოვთ მიუთითოთ ელ-ფოსტის კორექტული მისამართი.",url:"გთხოვთ მიუთითოთ კორექტული URL.",date:"გთხოვთ მიუთითოთ კორექტული თარიღი.",dateISO:"გთხოვთ მიუთითოთ კორექტული თარიღი ISO ფორმატში.",number:"გთხოვთ მიუთითოთ ციფრი.",digits:"გთხოვთ მიუთითოთ მხოლოდ ციფრები.",creditcard:"გთხოვთ მიუთითოთ საკრედიტო ბარათის კორექტული ნომერი.",equalTo:"გთხოვთ მიუთითოთ ასეთივე მნიშვნელობა კიდევ ერთხელ.",extension:"გთხოვთ აირჩიოთ ფაილი კორექტული გაფართოებით.",maxlength:a.validator.format("დასაშვებია არაუმეტეს {0} სიმბოლო."),minlength:a.validator.format("აუცილებელია შეიყვანოთ მინიმუმ {0} სიმბოლო."),rangelength:a.validator.format("ტექსტში სიმბოლოების რაოდენობა უნდა იყოს {0}-დან {1}-მდე."),range:a.validator.format("გთხოვთ შეიყვანოთ ციფრი {0}-დან {1}-მდე."),max:a.validator.format("გთხოვთ შეიყვანოთ ციფრი რომელიც ნაკლებია ან უდრის {0}-ს."),min:a.validator.format("გთხოვთ შეიყვანოთ ციფრი რომელიც მეტია ან უდრის {0}-ს.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Бұл өрісті міндетті түрде толтырыңыз.",remote:"Дұрыс мағына енгізуіңізді сұраймыз.",email:"Нақты электронды поштаңызды енгізуіңізді сұраймыз.",url:"Нақты URL-ды енгізуіңізді сұраймыз.",date:"Нақты URL-ды енгізуіңізді сұраймыз.",dateISO:"Нақты ISO форматымен сәйкес датасын енгізуіңізді сұраймыз.",number:"Күнді енгізуіңізді сұраймыз.",digits:"Тек қана сандарды енгізуіңізді сұраймыз.",creditcard:"Несие картасының нөмірін дұрыс енгізуіңізді сұраймыз.",equalTo:"Осы мәнді қайта енгізуіңізді сұраймыз.",extension:"Файлдың кеңейтуін дұрыс таңдаңыз.",maxlength:a.validator.format("Ұзындығы {0} символдан көр болмасын."),minlength:a.validator.format("Ұзындығы {0} символдан аз болмасын."),rangelength:a.validator.format("Ұзындығы {0}-{1} дейін мән енгізуіңізді сұраймыз."),range:a.validator.format("Пожалуйста, введите число от {0} до {1}. - {0} - {1} санын енгізуіңізді сұраймыз."),max:a.validator.format("{0} аз немесе тең санын енгізуіңіді сұраймыз."),min:a.validator.format("{0} көп немесе тең санын енгізуіңізді сұраймыз.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"필수 항목입니다.",remote:"항목을 수정하세요.",email:"유효하지 않은 E-Mail주소입니다.",url:"유효하지 않은 URL입니다.",date:"올바른 날짜를 입력하세요.",dateISO:"올바른 날짜(ISO)를 입력하세요.",number:"유효한 숫자가 아닙니다.",digits:"숫자만 입력 가능합니다.",creditcard:"신용카드 번호가 바르지 않습니다.",equalTo:"같은 값을 다시 입력하세요.",extension:"올바른 확장자가 아닙니다.",maxlength:a.validator.format("{0}자를 넘을 수 없습니다. "),minlength:a.validator.format("{0}자 이상 입력하세요."),rangelength:a.validator.format("문자 길이가 {0} 에서 {1} 사이의 값을 입력하세요."),range:a.validator.format("{0} 에서 {1} 사이의 값을 입력하세요."),max:a.validator.format("{0} 이하의 값을 입력하세요."),min:a.validator.format("{0} 이상의 값을 입력하세요.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Šis laukas yra privalomas.",remote:"Prašau pataisyti šį lauką.",email:"Prašau įvesti teisingą elektroninio pašto adresą.",url:"Prašau įvesti teisingą URL.",date:"Prašau įvesti teisingą datą.",dateISO:"Prašau įvesti teisingą datą (ISO).",number:"Prašau įvesti teisingą skaičių.",digits:"Prašau naudoti tik skaitmenis.",creditcard:"Prašau įvesti teisingą kreditinės kortelės numerį.",equalTo:"Prašau įvestį tą pačią reikšmę dar kartą.",extension:"Prašau įvesti reikšmę su teisingu plėtiniu.",maxlength:a.validator.format("Prašau įvesti ne daugiau kaip {0} simbolių."),minlength:a.validator.format("Prašau įvesti bent {0} simbolius."),rangelength:a.validator.format("Prašau įvesti reikšmes, kurių ilgis nuo {0} iki {1} simbolių."),range:a.validator.format("Prašau įvesti reikšmę intervale nuo {0} iki {1}."),max:a.validator.format("Prašau įvesti reikšmę mažesnę arba lygią {0}."),min:a.validator.format("Prašau įvesti reikšmę didesnę arba lygią {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Šis lauks ir obligāts.",remote:"Lūdzu, pārbaudiet šo lauku.",email:"Lūdzu, ievadiet derīgu e-pasta adresi.",url:"Lūdzu, ievadiet derīgu URL adresi.",date:"Lūdzu, ievadiet derīgu datumu.",dateISO:"Lūdzu, ievadiet derīgu datumu (ISO).",number:"Lūdzu, ievadiet derīgu numuru.",digits:"Lūdzu, ievadiet tikai ciparus.",creditcard:"Lūdzu, ievadiet derīgu kredītkartes numuru.",equalTo:"Lūdzu, ievadiet to pašu vēlreiz.",extension:"Lūdzu, ievadiet vērtību ar derīgu paplašinājumu.",maxlength:a.validator.format("Lūdzu, ievadiet ne vairāk kā {0} rakstzīmes."),minlength:a.validator.format("Lūdzu, ievadiet vismaz {0} rakstzīmes."),rangelength:a.validator.format("Lūdzu ievadiet {0} līdz {1} rakstzīmes."),range:a.validator.format("Lūdzu, ievadiet skaitli no {0} līdz {1}."),max:a.validator.format("Lūdzu, ievadiet skaitli, kurš ir mazāks vai vienāds ar {0}."),min:a.validator.format("Lūdzu, ievadiet skaitli, kurš ir lielāks vai vienāds ar {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Полето е задолжително.",remote:"Поправете го ова поле",email:"Внесете правилна e-mail адреса",url:"Внесете правилен URL.",date:"Внесете правилен датум",dateISO:"Внесете правилен датум (ISO).",number:"Внесете правилен број.",digits:"Внесете само бројки.",creditcard:"Внесете правилен број на кредитната картичка.",equalTo:"Внесете ја истата вредност повторно.",extension:"Внесете вредност со соодветна екстензија.",maxlength:a.validator.format("Внесете максимално {0} знаци."),minlength:a.validator.format("Внесете барем {0} знаци."),rangelength:a.validator.format("Внесете вредност со должина помеѓу {0} и {1} знаци."),range:a.validator.format("Внесете вредност помеѓу {0} и {1}."),max:a.validator.format("Внесете вредност помала или еднаква на {0}."),min:a.validator.format("Внесете вредност поголема или еднаква на {0}")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Medan ini diperlukan.",remote:"Sila betulkan medan ini.",email:"Sila masukkan alamat emel yang betul.",url:"Sila masukkan URL yang betul.",date:"Sila masukkan tarikh yang betul.",dateISO:"Sila masukkan tarikh(ISO) yang betul.",number:"Sila masukkan nombor yang betul.",digits:"Sila masukkan nilai digit sahaja.",creditcard:"Sila masukkan nombor kredit kad yang betul.",equalTo:"Sila masukkan nilai yang sama semula.",extension:"Sila masukkan nilai yang telah diterima.",maxlength:a.validator.format("Sila masukkan tidak lebih dari {0} aksara."),minlength:a.validator.format("Sila masukkan sekurang-kurangnya {0} aksara."),rangelength:a.validator.format("Sila masukkan antara {0} dan {1} panjang aksara."),range:a.validator.format("Sila masukkan nilai antara {0} dan {1} aksara."),max:a.validator.format("Sila masukkan nilai yang kurang atau sama dengan {0}."),min:a.validator.format("Sila masukkan nilai yang lebih atau sama dengan {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Dit is een verplicht veld.",remote:"Controleer dit veld.",email:"Vul hier een geldig e-mailadres in.",url:"Vul hier een geldige URL in.",date:"Vul hier een geldige datum in.",dateISO:"Vul hier een geldige datum in (ISO-formaat).",number:"Vul hier een geldig getal in.",digits:"Vul hier alleen getallen in.",creditcard:"Vul hier een geldig creditcardnummer in.",equalTo:"Vul hier dezelfde waarde in.",extension:"Vul hier een waarde in met een geldige extensie.",maxlength:a.validator.format("Vul hier maximaal {0} tekens in."),minlength:a.validator.format("Vul hier minimaal {0} tekens in."),rangelength:a.validator.format("Vul hier een waarde in van minimaal {0} en maximaal {1} tekens."),range:a.validator.format("Vul hier een waarde in van minimaal {0} en maximaal {1}."),max:a.validator.format("Vul hier een waarde in kleiner dan of gelijk aan {0}."),min:a.validator.format("Vul hier een waarde in groter dan of gelijk aan {0}."),step:a.validator.format("Vul hier een veelvoud van {0} in."),iban:"Vul hier een geldig IBAN in.",dateNL:"Vul hier een geldige datum in.",phoneNL:"Vul hier een geldig Nederlands telefoonnummer in.",mobileNL:"Vul hier een geldig Nederlands mobiel telefoonnummer in.",postalcodeNL:"Vul hier een geldige postcode in.",bankaccountNL:"Vul hier een geldig bankrekeningnummer in.",giroaccountNL:"Vul hier een geldig gironummer in.",bankorgiroaccountNL:"Vul hier een geldig bank- of gironummer in."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Angi en verdi.",remote:"Ugyldig verdi.",email:"Angi en gyldig epostadresse.",url:"Angi en gyldig URL.",date:"Angi en gyldig dato.",dateISO:"Angi en gyldig dato (&ARING;&ARING;&ARING;&ARING;-MM-DD).",number:"Angi et gyldig tall.",digits:"Skriv kun tall.",equalTo:"Skriv samme verdi igjen.",maxlength:a.validator.format("Maksimalt {0} tegn."),minlength:a.validator.format("Minimum {0} tegn."),rangelength:a.validator.format("Angi minimum {0} og maksimum {1} tegn."),range:a.validator.format("Angi en verdi mellom {0} og {1}."),max:a.validator.format("Angi en verdi som er mindre eller lik {0}."),min:a.validator.format("Angi en verdi som er st&oslash;rre eller lik {0}."),step:a.validator.format("Angi en verdi ganger {0}."),creditcard:"Angi et gyldig kredittkortnummer."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"To pole jest wymagane.",remote:"Proszę o wypełnienie tego pola.",email:"Proszę o podanie prawidłowego adresu email.",url:"Proszę o podanie prawidłowego URL.",date:"Proszę o podanie prawidłowej daty.",dateISO:"Proszę o podanie prawidłowej daty (ISO).",number:"Proszę o podanie prawidłowej liczby.",digits:"Proszę o podanie samych cyfr.",creditcard:"Proszę o podanie prawidłowej karty kredytowej.",equalTo:"Proszę o podanie tej samej wartości ponownie.",extension:"Proszę o podanie wartości z prawidłowym rozszerzeniem.",nipPL:"Proszę o podanie prawidłowego numeru NIP.",phonePL:"Proszę o podanie prawidłowego numeru telefonu",maxlength:a.validator.format("Proszę o podanie nie więcej niż {0} znaków."),minlength:a.validator.format("Proszę o podanie przynajmniej {0} znaków."),rangelength:a.validator.format("Proszę o podanie wartości o długości od {0} do {1} znaków."),range:a.validator.format("Proszę o podanie wartości z przedziału od {0} do {1}."),max:a.validator.format("Proszę o podanie wartości mniejszej bądź równej {0}."),min:a.validator.format("Proszę o podanie wartości większej bądź równej {0}."),pattern:a.validator.format("Pole zawiera niedozwolone znaki.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Este campo &eacute; requerido.",remote:"Por favor, corrija este campo.",email:"Por favor, forne&ccedil;a um endere&ccedil;o de email v&aacute;lido.",url:"Por favor, forne&ccedil;a uma URL v&aacute;lida.",date:"Por favor, forne&ccedil;a uma data v&aacute;lida.",dateISO:"Por favor, forne&ccedil;a uma data v&aacute;lida (ISO).",number:"Por favor, forne&ccedil;a um n&uacute;mero v&aacute;lido.",digits:"Por favor, forne&ccedil;a somente d&iacute;gitos.",creditcard:"Por favor, forne&ccedil;a um cart&atilde;o de cr&eacute;dito v&aacute;lido.",equalTo:"Por favor, forne&ccedil;a o mesmo valor novamente.",maxlength:a.validator.format("Por favor, forne&ccedil;a n&atilde;o mais que {0} caracteres."),minlength:a.validator.format("Por favor, forne&ccedil;a ao menos {0} caracteres."),rangelength:a.validator.format("Por favor, forne&ccedil;a um valor entre {0} e {1} caracteres de comprimento."),range:a.validator.format("Por favor, forne&ccedil;a um valor entre {0} e {1}."),max:a.validator.format("Por favor, forne&ccedil;a um valor menor ou igual a {0}."),min:a.validator.format("Por favor, forne&ccedil;a um valor maior ou igual a {0}."),step:a.validator.format("Por favor, forne&ccedil;a um valor m&uacute;ltiplo de {0}."),maxWords:a.validator.format("Por favor, forne&ccedil;a com {0} palavras ou menos."),minWords:a.validator.format("Por favor, forne&ccedil;a pelo menos {0} palavras."),rangeWords:a.validator.format("Por favor, forne&ccedil;a entre {0} e {1} palavras."),accept:"Por favor, forne&ccedil;a um tipo v&aacute;lido.",alphanumeric:"Por favor, forne&ccedil;a somente com letras, n&uacute;meros e sublinhados.",bankaccountNL:"Por favor, forne&ccedil;a com um n&uacute;mero de conta banc&aacute;ria v&aacute;lida.",bankorgiroaccountNL:"Por favor, forne&ccedil;a um banco v&aacute;lido ou n&uacute;mero de conta.",bic:"Por favor, forne&ccedil;a um c&oacute;digo BIC v&aacute;lido.",cifES:"Por favor, forne&ccedil;a um c&oacute;digo CIF v&aacute;lido.",creditcardtypes:"Por favor, forne&ccedil;a um n&uacute;mero de cart&atilde;o de cr&eacute;dito v&aacute;lido.",currency:"Por favor, forne&ccedil;a uma moeda v&aacute;lida.",dateFA:"Por favor, forne&ccedil;a uma data correta.",dateITA:"Por favor, forne&ccedil;a uma data correta.",dateNL:"Por favor, forne&ccedil;a uma data correta.",extension:"Por favor, forne&ccedil;a um valor com uma extens&atilde;o v&aacute;lida.",giroaccountNL:"Por favor, forne&ccedil;a um n&uacute;mero de conta corrente v&aacute;lido.",iban:"Por favor, forne&ccedil;a um c&oacute;digo IBAN v&aacute;lido.",integer:"Por favor, forne&ccedil;a um n&uacute;mero n&atilde;o decimal.",ipv4:"Por favor, forne&ccedil;a um IPv4 v&aacute;lido.",ipv6:"Por favor, forne&ccedil;a um IPv6 v&aacute;lido.",lettersonly:"Por favor, forne&ccedil;a apenas com letras.",letterswithbasicpunc:"Por favor, forne&ccedil;a apenas letras ou pontua&ccedil;ões.",mobileNL:"Por favor, fornece&ccedil;a um n&uacute;mero v&aacute;lido de telefone.",mobileUK:"Por favor, fornece&ccedil;a um n&uacute;mero v&aacute;lido de telefone.",nieES:"Por favor, forne&ccedil;a um NIE v&aacute;lido.",nifES:"Por favor, forne&ccedil;a um NIF v&aacute;lido.",nowhitespace:"Por favor, n&atilde;o utilize espa&ccedil;os em branco.",pattern:"O formato fornecido &eacute; inv&aacute;lido.",phoneNL:"Por favor, forne&ccedil;a um n&uacute;mero de telefone v&aacute;lido.",phoneUK:"Por favor, forne&ccedil;a um n&uacute;mero de telefone v&aacute;lido.",phoneUS:"Por favor, forne&ccedil;a um n&uacute;mero de telefone v&aacute;lido.",phonesUK:"Por favor, forne&ccedil;a um n&uacute;mero de telefone v&aacute;lido.",postalCodeCA:"Por favor, forne&ccedil;a um n&uacute;mero de c&oacute;digo postal v&aacute;lido.",postalcodeIT:"Por favor, forne&ccedil;a um n&uacute;mero de c&oacute;digo postal v&aacute;lido.",postalcodeNL:"Por favor, forne&ccedil;a um n&uacute;mero de c&oacute;digo postal v&aacute;lido.",postcodeUK:"Por favor, forne&ccedil;a um n&uacute;mero de c&oacute;digo postal v&aacute;lido.",postalcodeBR:"Por favor, forne&ccedil;a um CEP v&aacute;lido.",require_from_group:a.validator.format("Por favor, forne&ccedil;a pelo menos {0} destes campos."),skip_or_fill_minimum:a.validator.format("Por favor, optar entre ignorar esses campos ou preencher pelo menos {0} deles."),stateUS:"Por favor, forne&ccedil;a um estado v&aacute;lido.",strippedminlength:a.validator.format("Por favor, forne&ccedil;a pelo menos {0} caracteres."),time:"Por favor, forne&ccedil;a um hor&aacute;rio v&aacute;lido, no intervado de 00:00 a 23:59.",time12h:"Por favor, forne&ccedil;a um hor&aacute;rio v&aacute;lido, no intervado de 01:00 a 12:59 am/pm.",url2:"Por favor, forne&ccedil;a uma URL v&aacute;lida.",vinUS:"O n&uacute;mero de identifica&ccedil;&atilde;o de ve&iacute;culo informado (VIN) &eacute; inv&aacute;lido.",zipcodeUS:"Por favor, forne&ccedil;a um c&oacute;digo postal americano v&aacute;lido.",ziprange:"O c&oacute;digo postal deve estar entre 902xx-xxxx e 905xx-xxxx",cpfBR:"Por favor, forne&ccedil;a um CPF v&aacute;lido.",nisBR:"Por favor, forne&ccedil;a um NIS/PIS v&aacute;lido",cnhBR:"Por favor, forne&ccedil;a um CNH v&aacute;lido.",cnpjBR:"Por favor, forne&ccedil;a um CNPJ v&aacute;lido."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Campo de preenchimento obrigat&oacute;rio.",remote:"Por favor, corrija este campo.",email:"Por favor, introduza um endere&ccedil;o eletr&oacute;nico v&aacute;lido.",url:"Por favor, introduza um URL v&aacute;lido.",date:"Por favor, introduza uma data v&aacute;lida.",dateISO:"Por favor, introduza uma data v&aacute;lida (ISO).",number:"Por favor, introduza um n&uacute;mero v&aacute;lido.",digits:"Por favor, introduza apenas d&iacute;gitos.",creditcard:"Por favor, introduza um n&uacute;mero de cart&atilde;o de cr&eacute;dito v&aacute;lido.",equalTo:"Por favor, introduza de novo o mesmo valor.",extension:"Por favor, introduza um ficheiro com uma extens&atilde;o v&aacute;lida.",maxlength:a.validator.format("Por favor, n&atilde;o introduza mais do que {0} caracteres."),minlength:a.validator.format("Por favor, introduza pelo menos {0} caracteres."),rangelength:a.validator.format("Por favor, introduza entre {0} e {1} caracteres."),range:a.validator.format("Por favor, introduza um valor entre {0} e {1}."),max:a.validator.format("Por favor, introduza um valor menor ou igual a {0}."),min:a.validator.format("Por favor, introduza um valor maior ou igual a {0}."),nifES:"Por favor, introduza um NIF v&aacute;lido.",nieES:"Por favor, introduza um NIE v&aacute;lido.",cifES:"Por favor, introduza um CIF v&aacute;lido."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Acest câmp este obligatoriu.",remote:"Te rugăm să completezi acest câmp.",email:"Te rugăm să introduci o adresă de email validă",url:"Te rugăm sa introduci o adresă URL validă.",date:"Te rugăm să introduci o dată corectă.",dateISO:"Te rugăm să introduci o dată (ISO) corectă.",number:"Te rugăm să introduci un număr întreg valid.",digits:"Te rugăm să introduci doar cifre.",creditcard:"Te rugăm să introduci un numar de carte de credit valid.",equalTo:"Te rugăm să reintroduci valoarea.",extension:"Te rugăm să introduci o valoare cu o extensie validă.",maxlength:a.validator.format("Te rugăm să nu introduci mai mult de {0} caractere."),minlength:a.validator.format("Te rugăm să introduci cel puțin {0} caractere."),rangelength:a.validator.format("Te rugăm să introduci o valoare între {0} și {1} caractere."),range:a.validator.format("Te rugăm să introduci o valoare între {0} și {1}."),max:a.validator.format("Te rugăm să introduci o valoare egal sau mai mică decât {0}."),min:a.validator.format("Te rugăm să introduci o valoare egal sau mai mare decât {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Это поле необходимо заполнить.",remote:"Пожалуйста, введите правильное значение.",email:"Пожалуйста, введите корректный адрес электронной почты.",url:"Пожалуйста, введите корректный URL.",date:"Пожалуйста, введите корректную дату.",dateISO:"Пожалуйста, введите корректную дату в формате ISO.",number:"Пожалуйста, введите число.",digits:"Пожалуйста, вводите только цифры.",creditcard:"Пожалуйста, введите правильный номер кредитной карты.",equalTo:"Пожалуйста, введите такое же значение ещё раз.",extension:"Пожалуйста, выберите файл с правильным расширением.",maxlength:a.validator.format("Пожалуйста, введите не больше {0} символов."),minlength:a.validator.format("Пожалуйста, введите не меньше {0} символов."),rangelength:a.validator.format("Пожалуйста, введите значение длиной от {0} до {1} символов."),range:a.validator.format("Пожалуйста, введите число от {0} до {1}."),max:a.validator.format("Пожалуйста, введите число, меньшее или равное {0}."),min:a.validator.format("Пожалуйста, введите число, большее или равное {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"هنن جاين جي ضرورت آهي",remote:"هنن جاين جي ضرورت آهي",email:"لکيل اي ميل غلط آهي",url:"لکيل ايڊريس غلط آهي",date:"لکيل تاريخ غلط آهي",dateISO:"جي معيار جي مطابق نه آهي (ISO) لکيل تاريخ",number:"لکيل انگ صحيح ناهي",digits:"رڳو انگ داخل ڪري سگهجي ٿو",creditcard:"لکيل ڪارڊ نمبر صحيح نه آهي",equalTo:"داخل ٿيل ڀيٽ صحيح نه آهي",extension:"لکيل غلط آهي",maxlength:a.validator.format("وڌ کان وڌ {0} جي داخلا ڪري سگهجي ٿي"),minlength:a.validator.format("گهٽ ۾ گهٽ {0} جي داخلا ڪرڻ ضروري آهي"),rangelength:a.validator.format("داخلا جو {0} ۽ {1}جي وچ ۾ هجڻ ضروري آهي"),range:a.validator.format("داخلا جو {0} ۽ {1}جي وچ ۾ هجڻ ضروري آهي"),max:a.validator.format("وڌ کان وڌ {0} جي داخلا ڪري سگهجي ٿي"),min:a.validator.format("گهٽ ۾ گهٽ {0} جي داخلا ڪرڻ ضروري آهي")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"To polje je obvezno.",remote:"Vpis v tem polju ni v pravi obliki.",email:"Prosimo, vnesite pravi email naslov.",url:"Prosimo, vnesite pravi URL.",date:"Prosimo, vnesite pravi datum.",dateISO:"Prosimo, vnesite pravi datum (ISO).",number:"Prosimo, vnesite pravo številko.",digits:"Prosimo, vnesite samo številke.",creditcard:"Prosimo, vnesite pravo številko kreditne kartice.",equalTo:"Prosimo, ponovno vnesite enako vsebino.",extension:"Prosimo, vnesite vsebino z pravo končnico.",maxlength:a.validator.format("Prosimo, da ne vnašate več kot {0} znakov."),minlength:a.validator.format("Prosimo, vnesite vsaj {0} znakov."),rangelength:a.validator.format("Prosimo, vnesite od {0} do {1} znakov."),range:a.validator.format("Prosimo, vnesite vrednost med {0} in {1}."),max:a.validator.format("Prosimo, vnesite vrednost manjšo ali enako {0}."),min:a.validator.format("Prosimo, vnesite vrednost večjo ali enako {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Povinné zadať.",maxlength:a.validator.format("Maximálne {0} znakov."),minlength:a.validator.format("Minimálne {0} znakov."),rangelength:a.validator.format("Minimálne {0} a maximálne {1} znakov."),email:"E-mailová adresa musí byť platná.",url:"URL musí byť platná.",date:"Musí byť dátum.",number:"Musí byť číslo.",digits:"Môže obsahovať iba číslice.",equalTo:"Dve hodnoty sa musia rovnať.",range:a.validator.format("Musí byť medzi {0} a {1}."),max:a.validator.format("Nemôže byť viac ako {0}."),min:a.validator.format("Nemôže byť menej ako {0}."),creditcard:"Číslo platobnej karty musí byť platné.",step:a.validator.format("Musí byť násobkom čísla {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"To polje je obvezno.",remote:"Prosimo popravite to polje.",email:"Prosimo vnesite veljaven email naslov.",url:"Prosimo vnesite veljaven URL naslov.",date:"Prosimo vnesite veljaven datum.",dateISO:"Prosimo vnesite veljaven ISO datum.",number:"Prosimo vnesite veljavno število.",digits:"Prosimo vnesite samo števila.",creditcard:"Prosimo vnesite veljavno številko kreditne kartice.",equalTo:"Prosimo ponovno vnesite vrednost.",extension:"Prosimo vnesite vrednost z veljavno končnico.",maxlength:a.validator.format("Prosimo vnesite največ {0} znakov."),minlength:a.validator.format("Prosimo vnesite najmanj {0} znakov."),rangelength:a.validator.format("Prosimo vnesite najmanj {0} in največ {1} znakov."),range:a.validator.format("Prosimo vnesite vrednost med {0} in {1}."),max:a.validator.format("Prosimo vnesite vrednost manjše ali enako {0}."),min:a.validator.format("Prosimo vnesite vrednost večje ali enako {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Поље је обавезно.",remote:"Средите ово поље.",email:"Унесите исправну и-мејл адресу.",url:"Унесите исправан URL.",date:"Унесите исправан датум.",dateISO:"Унесите исправан датум (ISO).",number:"Унесите исправан број.",digits:"Унесите само цифе.",creditcard:"Унесите исправан број кредитне картице.",equalTo:"Унесите исту вредност поново.",extension:"Унесите вредност са одговарајућом екстензијом.",maxlength:a.validator.format("Унесите мање од {0} карактера."),minlength:a.validator.format("Унесите барем {0} карактера."),rangelength:a.validator.format("Унесите вредност дугачку између {0} и {1} карактера."),range:a.validator.format("Унесите вредност између {0} и {1}."),max:a.validator.format("Унесите вредност мању или једнаку {0}."),min:a.validator.format("Унесите вредност већу или једнаку {0}."),step:a.validator.format("Унесите вредност која је умножак броја {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Polje je obavezno.",remote:"Sredite ovo polje.",email:"Unesite ispravnu e-mail adresu",url:"Unesite ispravan URL.",date:"Unesite ispravan datum.",dateISO:"Unesite ispravan datum (ISO).",number:"Unesite ispravan broj.",digits:"Unesite samo cifre.",creditcard:"Unesite ispravan broj kreditne kartice.",equalTo:"Unesite istu vrednost ponovo.",extension:"Unesite vrednost sa odgovarajućom ekstenzijom.",maxlength:a.validator.format("Unesite manje od {0} karaktera."),minlength:a.validator.format("Unesite barem {0} karaktera."),rangelength:a.validator.format("Unesite vrednost dugačku između {0} i {1} karaktera."),range:a.validator.format("Unesite vrednost između {0} i {1}."),max:a.validator.format("Unesite vrednost manju ili jednaku {0}."),min:a.validator.format("Unesite vrednost veću ili jednaku {0}."),step:a.validator.format("Unesite vrednost koja je umnožak broja {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Detta f&auml;lt &auml;r obligatoriskt.",remote:"Var snäll och åtgärda detta fält.",maxlength:a.validator.format("Du f&aring;r ange h&ouml;gst {0} tecken."),minlength:a.validator.format("Du m&aring;ste ange minst {0} tecken."),rangelength:a.validator.format("Ange minst {0} och max {1} tecken."),email:"Ange en korrekt e-postadress.",url:"Ange en korrekt URL.",date:"Ange ett korrekt datum.",dateISO:"Ange ett korrekt datum (&Aring;&Aring;&Aring;&Aring;-MM-DD).",number:"Ange ett korrekt nummer.",digits:"Ange endast siffror.",equalTo:"Ange samma v&auml;rde igen.",range:a.validator.format("Ange ett v&auml;rde mellan {0} och {1}."),max:a.validator.format("Ange ett v&auml;rde som &auml;r mindre eller lika med {0}."),min:a.validator.format("Ange ett v&auml;rde som &auml;r st&ouml;rre eller lika med {0}."),creditcard:"Ange ett korrekt kreditkortsnummer.",pattern:"Ogiltigt format."}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"โปรดระบุ",remote:"โปรดแก้ไขให้ถูกต้อง",email:"โปรดระบุที่อยู่อีเมล์ที่ถูกต้อง",url:"โปรดระบุ URL ที่ถูกต้อง",date:"โปรดระบุวันที่ ที่ถูกต้อง",dateISO:"โปรดระบุวันที่ ที่ถูกต้อง (ระบบ ISO).",number:"โปรดระบุทศนิยมที่ถูกต้อง",digits:"โปรดระบุจำนวนเต็มที่ถูกต้อง",creditcard:"โปรดระบุรหัสบัตรเครดิตที่ถูกต้อง",equalTo:"โปรดระบุค่าเดิมอีกครั้ง",extension:"โปรดระบุค่าที่มีส่วนขยายที่ถูกต้อง",maxlength:a.validator.format("โปรดอย่าระบุค่าที่ยาวกว่า {0} อักขระ"),minlength:a.validator.format("โปรดอย่าระบุค่าที่สั้นกว่า {0} อักขระ"),rangelength:a.validator.format("โปรดอย่าระบุค่าความยาวระหว่าง {0} ถึง {1} อักขระ"),range:a.validator.format("โปรดระบุค่าระหว่าง {0} และ {1}"),max:a.validator.format("โปรดระบุค่าน้อยกว่าหรือเท่ากับ {0}"),min:a.validator.format("โปรดระบุค่ามากกว่าหรือเท่ากับ {0}")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Ворид кардани ин филд маҷбури аст.",remote:"Илтимос, маълумоти саҳеҳ ворид кунед.",email:"Илтимос, почтаи электронии саҳеҳ ворид кунед.",url:"Илтимос, URL адреси саҳеҳ ворид кунед.",date:"Илтимос, таърихи саҳеҳ ворид кунед.",dateISO:"Илтимос, таърихи саҳеҳи (ISO)ӣ ворид кунед.",number:"Илтимос, рақамҳои саҳеҳ ворид кунед.",digits:"Илтимос, танҳо рақам ворид кунед.",creditcard:"Илтимос, кредит карди саҳеҳ ворид кунед.",equalTo:"Илтимос, миқдори баробар ворид кунед.",extension:"Илтимос, қофияи файлро дуруст интихоб кунед",maxlength:a.validator.format("Илтимос, бештар аз {0} рамз ворид накунед."),minlength:a.validator.format("Илтимос, камтар аз {0} рамз ворид накунед."),rangelength:a.validator.format("Илтимос, камтар аз {0} ва зиёда аз {1} рамз ворид кунед."),range:a.validator.format("Илтимос, аз {0} то {1} рақам зиёд ворид кунед."),max:a.validator.format("Илтимос, бештар аз {0} рақам ворид накунед."),min:a.validator.format("Илтимос, камтар аз {0} рақам ворид накунед.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Bu alanın doldurulması zorunludur.",remote:"Lütfen bu alanı düzeltin.",email:"Lütfen geçerli bir e-posta adresi giriniz.",url:"Lütfen geçerli bir web adresi (URL) giriniz.",date:"Lütfen geçerli bir tarih giriniz.",dateISO:"Lütfen geçerli bir tarih giriniz(ISO formatında)",number:"Lütfen geçerli bir sayı giriniz.",digits:"Lütfen sadece sayısal karakterler giriniz.",creditcard:"Lütfen geçerli bir kredi kartı giriniz.",equalTo:"Lütfen aynı değeri tekrar giriniz.",extension:"Lütfen geçerli uzantıya sahip bir değer giriniz.",phone:"Lütfen geçerli bir telefon numarası giriniz.",maxlength:a.validator.format("Lütfen en fazla {0} karakter uzunluğunda bir değer giriniz."),minlength:a.validator.format("Lütfen en az {0} karakter uzunluğunda bir değer giriniz."),rangelength:a.validator.format("Lütfen en az {0} ve en fazla {1} uzunluğunda bir değer giriniz."),range:a.validator.format("Lütfen {0} ile {1} arasında bir değer giriniz."),max:a.validator.format("Lütfen {0} değerine eşit ya da daha küçük bir değer giriniz."),min:a.validator.format("Lütfen {0} değerine eşit ya da daha büyük bir değer giriniz."),require_from_group:a.validator.format("Lütfen bu alanların en az {0} tanesini doldurunuz.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Це поле необхідно заповнити.",remote:"Будь ласка, введіть правильне значення.",email:"Будь ласка, введіть коректну адресу електронної пошти.",url:"Будь ласка, введіть коректний URL.",date:"Будь ласка, введіть коректну дату.",dateISO:"Будь ласка, введіть коректну дату у форматі ISO.",number:"Будь ласка, введіть число.",digits:"Вводите потрібно лише цифри.",creditcard:"Будь ласка, введіть правильний номер кредитної карти.",equalTo:"Будь ласка, введіть таке ж значення ще раз.",extension:"Будь ласка, виберіть файл з правильним розширенням.",maxlength:a.validator.format("Будь ласка, введіть не більше {0} символів."),minlength:a.validator.format("Будь ласка, введіть не менше {0} символів."),rangelength:a.validator.format("Будь ласка, введіть значення довжиною від {0} до {1} символів."),range:a.validator.format("Будь ласка, введіть число від {0} до {1}."),max:a.validator.format("Будь ласка, введіть число, менше або рівно {0}."),min:a.validator.format("Будь ласка, введіть число, більше або рівно {0}.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"ان معلومات کا اندراج ضروری ہے",remote:"ان معلومات کا اندراج ضروری ہے",email:"درج کی ہوئی ای میل درست نہیں ہے",url:"درج کیا گیا پتہ درست نہیں ہے",date:"درج کی گئی تاریخ درست نہیں ہے",dateISO:"معیار کے مطابق نہیں ہے (ISO) درج کی گئی تاریخ",number:"درج کیےگئے ہندسے درست نہیں ہیں",digits:"صرف ہندسے اندراج کئے جاسکتے ہیں",creditcard:"درج کیا گیا کارڈ نمبر درست نہیں ہے",equalTo:"اندراج کا موازنہ درست نہیں ہے",extension:"اندراج درست نہیں ہے",maxlength:a.validator.format("زیادہ سے زیادہ {0} کا اندراج کر سکتے ہیں"),minlength:a.validator.format("کم سے کم {0} کا اندراج کرنا ضروری ہے"),rangelength:a.validator.format("اندراج کا {0} اور {1}کے درمیان ہونا ضروری ہے"),range:a.validator.format("اندراج کا {0} اور {1} کے درمیان ہونا ضروری ہے"),max:a.validator.format("زیادہ سے زیادہ {0} کا اندراج کر سکتے ہیں"),min:a.validator.format("کم سے کم {0} کا اندراج کرنا ضروری ہے")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"Hãy nhập.",remote:"Hãy sửa cho đúng.",email:"Hãy nhập email.",url:"Hãy nhập URL.",date:"Hãy nhập ngày.",dateISO:"Hãy nhập ngày (ISO).",number:"Hãy nhập số.",digits:"Hãy nhập chữ số.",creditcard:"Hãy nhập số thẻ tín dụng.",equalTo:"Hãy nhập thêm lần nữa.",extension:"Phần mở rộng không đúng.",maxlength:a.validator.format("Hãy nhập từ {0} kí tự trở xuống."),minlength:a.validator.format("Hãy nhập từ {0} kí tự trở lên."),rangelength:a.validator.format("Hãy nhập từ {0} đến {1} kí tự."),range:a.validator.format("Hãy nhập từ {0} đến {1}."),max:a.validator.format("Hãy nhập từ {0} trở xuống."),min:a.validator.format("Hãy nhập từ {0} trở lên.")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"这是必填字段",remote:"请修正此字段",email:"请输入有效的电子邮件地址",url:"请输入有效的网址",date:"请输入有效的日期",dateISO:"请输入有效的日期 (YYYY-MM-DD)",number:"请输入有效的数字",digits:"只能输入数字",creditcard:"请输入有效的信用卡号码",equalTo:"你的输入不相同",extension:"请输入有效的后缀",maxlength:a.validator.format("最多可以输入 {0} 个字符"),minlength:a.validator.format("最少要输入 {0} 个字符"),rangelength:a.validator.format("请输入长度在 {0} 到 {1} 之间的字符串"),range:a.validator.format("请输入范围在 {0} 到 {1} 之间的数值"),step:a.validator.format("请输入 {0} 的整数倍值"),max:a.validator.format("请输入不大于 {0} 的数值"),min:a.validator.format("请输入不小于 {0} 的数值")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.messages,{required:"必須填寫",remote:"請修正此欄位",email:"請輸入有效的電子郵件",url:"請輸入有效的網址",date:"請輸入有效的日期",dateISO:"請輸入有效的日期 (YYYY-MM-DD)",number:"請輸入正確的數值",digits:"只可輸入數字",creditcard:"請輸入有效的信用卡號碼",equalTo:"請重複輸入一次",extension:"請輸入有效的後綴",maxlength:a.validator.format("最多 {0} 個字"),minlength:a.validator.format("最少 {0} 個字"),rangelength:a.validator.format("請輸入長度為 {0} 至 {1} 之間的字串"),range:a.validator.format("請輸入 {0} 至 {1} 之間的數值"),step:a.validator.format("請輸入 {0} 的整數倍值"),max:a.validator.format("請輸入不大於 {0} 的數值"),min:a.validator.format("請輸入不小於 {0} 的數值")}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(a)}}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d\d?\-\d\d?\-\d\d\d?\d?$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(a)}}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d{1,2}\.\d{1,2}\.\d{4}$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+)(?:,\d+)?$/.test(a)}}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d\d?\-\d\d?\-\d\d\d?\d?$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(a)}}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d\d?[\.\/\-]\d\d?[\.\/\-]\d\d\d?\d?$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(a)}}),a});

View File

@ -0,0 +1,4 @@
/*! jQuery Validation Plugin - v1.19.3 - 1/9/2021
* https://jqueryvalidation.org/
* Copyright (c) 2021 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery","../jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return a.extend(a.validator.methods,{date:function(a,b){return this.optional(b)||/^\d\d?\/\d\d?\/\d\d\d?\d?$/.test(a)}}),a});

2
client/dist/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
client/dist/styles/cms.css vendored Normal file
View File

@ -0,0 +1 @@
.cms table.ss-gridfield-table tr.spam{background-color:#ffd8d8}.cms table.ss-gridfield-table tr.spam:hover{background-color:#fff2f2}

1
client/dist/styles/comments.css vendored Normal file
View File

@ -0,0 +1 @@
.comments-holder-container{clear:both}.comments-holder-container .field{clear:left}.comments-holder-container .author,.comments-holder-container .num{font-size:1.3em}.comments-holder-container .num{color:#999;margin-right:5px}.comments-holder-container .num-total{line-height:40px;margin-bottom:0}.comments-holder-container #moderated{border:1px solid #999;padding:20px;font-style:italic}.comments-holder-container .comments-list{margin:0}.comments-holder-container .comment-count{margin:15px 0}.comments-holder-container .commenting-area{margin-top:50px}.comments-holder-container .commenting-area label.left{font-weight:400}.comments-holder-container .commenting-rss-feed{margin-top:4em;text-align:right}.comments-holder-container .commenting-rss-feed .action-links{margin:20px 0 10px}.comments-holder-container .commenting-rss-feed .action-links li{display:inline;list-style-type:none;margin-left:20px}.comments-holder-container .commenting-rss-feed .action-links li:first-child{margin-left:0}.comments-holder-container .commenting-rss-feed .action-links li.comment-reply-action{float:right}.comments-holder-container .commenting-rss-feed .action-links a{background-image:none}.comments-holder-container .commenting-rss-feed .action-links a:first-letter{text-transform:capitalize}.comments-holder-container .no-comments-yet{display:inline-block;margin-top:10px}.comment{clear:both;list-style-type:none;overflow:hidden;padding:20px 0;position:relative}.comment.author-comment:after{content:"Author";float:right;position:absolute;top:1.5em;right:.5em;font-size:1em;font-weight:700}.comment.author-comment>.comment-text{border:1px solid blue}.comment.spam .comment-text{border:1px dashed orange}.comment img.gravatar{float:left;margin:20px;width:90px;height:90px;border:none}.comment .comment-text{background-color:#fff;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none;margin:0;padding:0 20px;min-height:130px;white-space:pre;white-space:pre-wrap;white-space:pre-line;word-wrap:break-word}.comment .comment-text p:last-child{margin-bottom:0}.comment .comment-text.hasGravatar{padding:0 20px 0 130px;min-height:130px}.comment .date{font-size:16px}.comment .date:before{content:"\A0\A0\A0\A0"}.comment.unmoderated>.comment-text{border:1px solid #ff0}.comment .info{margin-bottom:10px}.comment.spam .comment{border:1px dashed orange;border-radius:4px;padding:2.5em 1em 1em}.comment .comment-replies-container{clear:both;padding-left:10px;margin:80px 0 0;border-left:1px dashed #999}.comment .comment-replies-container .comment-reply-form-holder{padding:0 10px}.comment .comment-replies-container .comment-replies-holder{padding:0 0 0 10px}.comments-list.root-level{margin-left:0}.comment-reply-link{float:right}.comment-moderation-options{float:left}.comment-action-links{margin:20px 0 10px}.comment-action-links a:not(:last-child){margin-right:12px}.commenting-rss-feed ul{list-style:none}

10
client/lang/en.js Normal file
View File

@ -0,0 +1,10 @@
// This file was generated by silverstripe/cow from javascript/lang/src/en.js.
// See https://github.com/tractorcow/cow for details
if (typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if (typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('en', {
"CommentsInterface_singlecomment_ss.DELETE_CONFIRMATION": "Are you sure you want to delete this comment?",
"CommentsInterface_singlecomment_ss.AJAX_ERROR": "An error occurred whilst updating the comment",
});
}

4
client/lang/src/en.json Normal file
View File

@ -0,0 +1,4 @@
{
"CommentsInterface_singlecomment_ss.DELETE_CONFIRMATION": "Are you sure you want to delete this comment?",
"CommentsInterface_singlecomment_ss.AJAX_ERROR": "An error occurred whilst updating the comment",
}

View File

@ -0,0 +1,133 @@
/**
* @package comments
*/
(function($) {
// The above closure encapsulates the $ variable away from the global scope
// and the one below is the `$(document).ready(...)` shorthand.
$(function() {
// Override the default URL validator in order to extend it to allow protocol-less URLs
$.validator.methods.url = function( value, element ) {
// This line is copied directly from the jQuery.validation source (version 1.19.0)
// the only change is a single question mark added here ---------v
return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value );
}
/**
* Enable form validation
*/
$('.comments-holder-container form').each(function() {
$(this).validate({
// Ignore hidden elements in this form
ignore: ':hidden',
// Use default 'required' for error labels
errorClass: "required",
// Use span instead of labels
errorElement: "span",
// On error, scroll to the invalid element
invalidHandler : function(form, validator){
$('html, body').animate({
scrollTop: $(validator.errorList[0].element).offset().top - 30
}, 200);
},
// Ensure any new error message has the correct class and placement
errorPlacement: function(error, element) {
error
.addClass('message')
.insertAfter(element);
}
});
});
/**
* Hide comment reply forms by default (unless visiting via permalink)
*/
$(".comment")
.children('.info')
.not(window.document.location.hash)
.nextAll(".comment-replies-container")
.children(".comment-reply-form-holder")
.hide();
/**
* Toggle on/off reply form
*/
$('.comments-holder').on('click', '.comment-reply-link', function(e) {
var allForms = $('.comment-reply-form-holder');
var formID = '#' + $(this).attr('aria-controls');
var form = $(formID).closest('.comment-reply-form-holder');
$(this).attr('aria-expanded', function (i, attr) {
return attr == 'true' ? 'false' : 'true'
});
// Prevent focus
e.preventDefault();
if(form.is(':visible')) {
allForms.slideUp();
} else {
allForms.not(form).slideUp();
form.slideDown();
}
});
/**
* Clicking one of the metalinks performs the operation via ajax
* this inclues the spam and approve links
*/
$('.comments-holder .comments-list').on('click', 'div.comment-moderation-options a', function(e) {
e.stopPropagation();
var link = $(this);
if (link.hasClass('delete')) {
var confirmationMsg = ss.i18n._t('CommentsInterface_singlecomment_ss.DELETE_CONFIRMATION');
var confirmation = window.confirm(confirmationMsg);
if (!confirmation) {
e.preventDefault();
return false;
}
}
var comment = link.parents('.comment:first');
$.ajax({
url: $(this).attr('href'),
cache: false,
success: function(html){
if(link.hasClass('ham')) {
// comment has been marked as not spam
comment.html(html);
comment.removeClass('spam');
}
else if(link.hasClass('approve')) {
// comment has been approved
comment.html(html);
comment.removeClass('unmoderated');
}
else if(link.hasClass('delete')) {
comment.fadeOut(1000, function() {
comment.remove();
if($('.comments-holder .comments-list').children().length === 0) {
$('.no-comments-yet').show();
}
});
}
else if(link.hasClass('spam')) {
comment.html(html).addClass('spam');
}
},
failure: function(html) {
var errorMsg = ss.i18n._t('CommentsInterface_singlecomment_ss.AJAX_ERROR');
alert(errorMsg);
}
});
e.preventDefault();
});
});
})(jQuery);

View File

@ -0,0 +1,7 @@
.cms table.ss-gridfield-table tr.spam {
background-color: #FFD8D8;
}
.cms table.ss-gridfield-table tr.spam:hover {
background-color: #FFF2F2;
}

View File

@ -0,0 +1,208 @@
.comments-holder-container {
clear: both;
.field {
clear: left;
}
.num,
.author {
font-size: 1.3em;
}
.num {
color: #999;
margin-right: 5px;
}
.num-total {
line-height: 40px;
margin-bottom: 0;
}
#moderated {
border: solid #999 1px;
padding: 20px;
font-style: italic;
}
.comments-list {
margin: 0;
}
.comment-count {
margin: 15px 0;
}
// The comment form
.commenting-area {
margin-top: 50px;
label.left{
font-weight: normal;
}
}
.commenting-rss-feed {
margin-top: 4em;
text-align: right;
.action-links {
margin: 20px 0 10px;
li {
display: inline;
list-style-type: none;
margin-left: 20px;
&:first-child {
margin-left: 0;
}
&.comment-reply-action {
float: right;
}
}
a {
background-image: none;
&::first-letter {
text-transform: capitalize;
}
}
}
}
.no-comments-yet {
display: inline-block;
margin-top: 10px;
}
}
// A published comment
.comment {
clear:both;
list-style-type: none;
overflow: hidden;
padding: 20px 0 20px 0;
position: relative;
&.author-comment {
&:after {
content: 'Author';
float: right;
position: absolute;
top: 1.5em;
right: 0.5em;
font-size: 1em;
font-weight: bold;
}
> .comment-text {
border: 1px solid blue;
}
}
&.spam {
.comment-text {
border: 1px dashed orange;
}
}
img.gravatar {
float: left;
margin: 20px;
width: 90px;
height: 90px;
border: none;
}
.comment-text {
background-color: #fff;
border: 1px solid #ddd;
box-shadow: none;
margin: 0;
padding: 0 20px 0 20px;
min-height: 130px;
white-space: pre;
white-space: pre-wrap;
white-space: pre-line;
word-wrap: break-word;
p:last-child {
margin-bottom: 0;
}
}
.comment-text.hasGravatar {
// leave enough space on the left for the default gravatar size
padding: 0 20px 0 130px;
// Ensure there is enough vertical space for the Gravatar
min-height: 130px;
}
.date {
font-size: 16px;
&:before {
content: '\0000a0\0000a0\0000a0\0000a0';
}
}
&.unmoderated {
> .comment-text {
border: 1px solid yellow;
}
}
.info {
margin-bottom: 10px;
}
&.spam .comment {
border: 1px dashed orange;
border-radius: 4px;
padding: 2.5em 1em 1em;
}
.comment-replies-container {
clear: both;
padding-left: 10px;
margin: 80px 0 0 0;
border-left: 1px dashed #999;
.comment-reply-form-holder {
padding: 0 10px; // Prevent clipping issues on slideUp/Down
}
.comment-replies-holder {
padding: 0 0 0 10px;
}
}
}
.comments-list.root-level {
margin-left: 0;
}
.comment-reply-link {
float: right;
}
.comment-moderation-options {
float: left;
}
.comment-action-links {
margin: 20px 0 10px;
a:not(:last-child) {
margin-right: 12px;
}
}
.commenting-rss-feed ul {
list-style: none;
}

1
code-of-conduct.md Normal file
View File

@ -0,0 +1 @@
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).

View File

@ -1,130 +0,0 @@
<?php
/**
* Comment administration system within the CMS
*
* @package comments
*/
class CommentAdmin extends LeftAndMain implements PermissionProvider {
private static $url_segment = 'comments';
private static $url_rule = '/$Action';
private static $menu_title = 'Comments';
private static $allowed_actions = array(
'approvedmarked',
'deleteall',
'deletemarked',
'hammarked',
'showtable',
'spammarked',
'EditForm',
'unmoderated'
);
public function providePermissions() {
return array(
"CMS_ACCESS_CommentAdmin" => array(
'name' => _t('CommentAdmin.ADMIN_PERMISSION', "Access to 'Comments' section"),
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
)
);
}
/**
* @return Form
*/
public function getEditForm($id = null, $fields = null) {
if(!$id) $id = $this->currentPageID();
$form = parent::getEditForm($id);
$record = $this->getRecord($id);
if($record && !$record->canView()) {
return Security::permissionFailure($this);
}
$commentsConfig = GridFieldConfig::create()->addComponents(
new GridFieldFilterHeader(),
$columns = new GridFieldDataColumns(),
new GridFieldSortableHeader(),
new GridFieldPaginator(25),
new GridFieldDeleteAction(),
new GridFieldDetailForm(),
new GridFieldExportButton(),
new GridFieldEditButton(),
new GridFieldDetailForm(),
$manager = new GridFieldBulkManager()
);
$manager->addBulkAction(
'markAsSpam', 'Mark as spam', 'CommentsGridFieldBulkAction_MarkAsSpam',
array(
'isAjax' => true,
'icon' => 'delete',
'isDestructive' => true
)
);
$columns->setFieldFormatting(array(
'ParentTitle' => function($value, &$item) {
return sprintf(
'<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>',
Convert::raw2xml($item->Link()),
Convert::raw2xml($value)
);
}
));
$needs = new GridField(
'Comments',
_t('CommentsAdmin.NeedsModeration', 'Needs Moderation'),
Comment::get()->filter('Moderated',0),
$commentsConfig
);
$moderated = new GridField(
'CommentsModerated',
_t('CommentsAdmin.CommentsModerated'),
Comment::get()->filter('Moderated',1),
$commentsConfig
);
$fields = new FieldList(
$root = new TabSet(
'Root',
new Tab('NeedsModeration', _t('CommentAdmin.NeedsModeration', 'Needs Moderation'),
$needs
),
new Tab('Comments', _t('CommentAdmin.Moderated', 'Moderated'),
$moderated
)
)
);
$root->setTemplate('CMSTabSet');
$actions = new FieldList();
$form = new Form(
$this,
'EditForm',
$fields,
$actions
);
$form->addExtraClass('cms-edit-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
if($form->Fields()->hasTabset()) {
$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
$form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses());
}
$this->extend('updateEditForm', $form);
return $form;
}
}

View File

@ -1,182 +0,0 @@
<?php
/**
* Helper Class for storing the configuration options. Retains the mapping between
* objects which have comments attached and the related configuration options.
*
* Also handles adding the Commenting extension to the {@link DataObject} on behalf
* of the user.
*
* For documentation on how to use this class see docs/en/Configuration.md
*
* @package comments
*/
class Commenting {
/**
* @var array map of enabled {@link DataObject} and related configuration
*/
private static $enabled_classes = array();
/**
* @var array default configuration values
*/
private static $default_config = array(
'require_login' => false, // boolean, whether a user needs to login
'required_permission' => false, // required permission to comment (or array of permissions)
'include_js' => true, // Enhance operation by ajax behaviour on moderation links
'use_gravatar' => false, // set to true to show gravatar icons,
'gravatar_size' => 80, // size of gravatar in pixels. This is the same as the standard default
'gravatar_default' => 'identicon', // theme for 'not found' gravatar (see http://gravatar.com/site/implement/images/)
'gravatar_rating' => 'g', // gravatar rating. This is the same as the standard default
'show_comments_when_disabled' => false, // when comments are disabled should we show older comments (if available)
'order_comments_by' => "\"Created\" DESC",
'comments_per_page' => 10,
'comments_holder_id' => "comments-holder", // id for the comments holder
'comment_permalink_prefix' => "comment-", // id prefix for each comment. If needed make this different
'require_moderation' => false,
'require_moderation_nonmembers' => false, // requires moderation for comments posted by non-members. 'require_moderation' overrides this if set.
'html_allowed' => false, // allow for sanitized HTML in comments
'html_allowed_elements' => array('a', 'img', 'i', 'b'),
'use_preview' => false, // preview formatted comment (when allowing HTML). Requires include_js=true
);
/**
* Adds commenting to a {@link DataObject}
*
* @param string classname to add commenting to
* @param array $setting Settings. See {@link self::$default_config} for
* available settings
*
* @throws InvalidArgumentException
*/
public static function add($class, $settings = false) {
if($settings && !is_array($settings)) {
throw new InvalidArgumentException('$settings needs to be an array or null', E_USER_ERROR);
}
self::$enabled_classes[$class] = $settings;
$class::add_extension('CommentsExtension');
}
/**
* Removes commenting from a {@link DataObject}. Does not remove existing comments
* but does remove the extension.
*
* @param string $class Class to remove {@link CommentsExtension} from
*/
public static function remove($class) {
if(isset(self::$enabled_classes[$class])) {
unset(self::$enabled_classes[$class]);
}
$class::remove_extension('CommentsExtension');
}
/**
* Returns whether a given class name has commenting enabled
*
* @return bool
*/
public static function has_commenting($class) {
return (isset(self::$enabled_classes[$class]));
}
/**
* Sets a value for a class of a given config setting. Passing 'all' as the class
* sets it for everything
*
* @param string $class Class to set the value on. Passing 'all' will set it to all
* active mappings
* @param string $key setting to change
* @param mixed $value value of the setting
*/
public static function set_config_value($class, $key, $value = false) {
if($class == "all") {
if($enabledClasses = self::$enabled_classes) {
foreach($enabledClasses as $enabled) {
if(!is_array($enabled)) $enabled = array();
$enabled[$key] = $value;
}
}
}
else if(isset(self::$enabled_classes[$class])) {
if(!is_array(self::$enabled_classes[$class])) self::$enabled_classes[$class] = array();
self::$enabled_classes[$class][$key] = $value;
}
else {
throw new Exception("$class does not have commenting enabled", E_USER_ERROR);
}
}
/**
* Returns a given config value for a commenting class
*
* @param string $class
* @param string $key config value to return
*
* @throws Exception
* @return mixed
*/
public static function get_config_value($class = null, $key) {
if(!$class || isset(self::$enabled_classes[$class])) {
// custom configuration
if(isset(self::$enabled_classes[$class][$key])) return self::$enabled_classes[$class][$key];
// default configuration
if(isset(self::$default_config[$key])) return self::$default_config[$key];
// config value doesn't exist
throw new Exception("Config ($key) is not a valid configuration value", E_USER_WARNING);
}
else {
throw new Exception("$class does not have commenting enabled", E_USER_ERROR);
}
}
/**
* Determines whether a config value on the commenting extension
* matches a given value.
*
* @param string $class
* @param string $key
* @param string $value Expected value
*
* @return bool
*/
public static function config_value_equals($class, $key, $value) {
try {
$check = self::get_config_value($class, $key);
if($check && ($check == $value)) return true;
}
catch(Exception $e) {}
return false;
}
/**
* Return whether a user can post on a given commenting instance
*
* @param string $class
*/
public static function can_member_post($class) {
$member = Member::currentUser();
try {
$login = self::get_config_value($class, 'require_login');
$permission = self::get_config_value($class, 'required_permission');
if($permission && !Permission::check($permission)) return false;
if($login && !$member) return false;
}
catch(Exception $e) {}
return true;
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @package comments
*/
class CommentsGridFieldBulkAction extends GridFieldBulkActionHandler {
}
/**
* A {@link GridFieldBulkActionHandler} for bulk marking comments as spam
*
* @package comments
*/
class CommentsGridFieldBulkAction_MarkAsSpam extends CommentsGridFieldBulkAction {
private static $allowed_actions = array(
'markAsSpam'
);
private static $url_handlers = array(
'markAsSpam' => 'markAsSpam'
);
public function markAsSpam(SS_HTTPRequest $request) {
$ids = array();
foreach($this->getRecords() as $record) {
array_push($ids, $record->ID);
$record->Moderated = 1;
$record->IsSpam = 1;
$record->write();
}
$response = new SS_HTTPResponse(Convert::raw2json(array(
'done' => true,
'records' => $ids
)));
$response->addHeader('Content-Type', 'text/json');
return $response;
}
}

View File

@ -1,447 +0,0 @@
<?php
/**
* @package comments
*/
class CommentingController extends Controller {
private static $allowed_actions = array(
'delete',
'spam',
'ham',
'approve',
'rss',
'CommentsForm',
'doPostComment',
'doPreviewComment'
);
private $baseClass = "";
private $ownerRecord = "";
private $ownerController = "";
public function setBaseClass($class) {
$this->baseClass = $class;
}
public function getBaseClass() {
return $this->baseClass;
}
public function setOwnerRecord($record) {
$this->ownerRecord = $record;
}
public function getOwnerRecord() {
return $this->ownerRecord;
}
public function setOwnerController($controller) {
$this->ownerController = $controller;
}
public function getOwnerController() {
return $this->ownerController;
}
/**
* Workaround for generating the link to this controller
*
* @return string
*/
public function Link($action = "", $id = '', $other = '') {
return Controller::join_links(__CLASS__ , $action, $id, $other);
}
/**
* Outputs the RSS feed of comments
*
* @return XML
*/
public function rss() {
return $this->getFeed($this->request)->outputToBrowser();
}
/**
* Return an RSSFeed of comments for a given set of comments or all
* comments on the website.
*
* To maintain backwards compatibility with 2.4 this supports mapping
* of PageComment/rss?pageid= as well as the new RSS format for comments
* of CommentingController/rss/{classname}/{id}
*
* @param SS_HTTPRequest
*
* @return RSSFeed
*/
public function getFeed(SS_HTTPRequest $request) {
$link = $this->Link('rss');
$class = $request->param('ID');
$id = $request->param('OtherID');
$comments = Comment::get()->filter(array(
'Moderated' => 1,
'IsSpam' => 0,
));
if($request->getVar('pageid')) {
$comments = $comments->filter(array(
'BaseClass' => 'SiteTree',
'ParentID' => $request->getVar('pageid'),
));
$link = $this->Link('rss', 'SiteTree', $id);
} elseif($class && $id) {
if(Commenting::has_commenting($class)) {
$comments = $comments->filter(array(
'BaseClass' => $class,
'ParentID' => $id,
));
$link = $this->Link('rss', Convert::raw2xml($class), (int) $id);
} else {
return $this->httpError(404);
}
} elseif($class) {
if(Commenting::has_commenting($class)) {
$comments = $comments->filter('BaseClass', $class);
} else {
return $this->httpError(404);
}
}
$title = _t('CommentingController.RSSTITLE', "Comments RSS Feed");
$comments = new PaginatedList($comments, $request);
$comments->setPageLength(Commenting::get_config_value(null, 'comments_per_page'));
return new RSSFeed(
$comments,
$link,
$title,
$link,
'Title', 'EscapedComment', 'AuthorName'
);
}
/**
* Deletes a given {@link Comment} via the URL.
*/
public function delete() {
if(!$this->checkSecurityToken($this->request)) {
return $this->httpError(400);
}
if(($comment = $this->getComment()) && $comment->canDelete()) {
$comment->delete();
return ($this->request->isAjax()) ? true : $this->redirectBack();
}
return $this->httpError(404);
}
/**
* Marks a given {@link Comment} as spam. Removes the comment from display
*/
public function spam() {
if(!$this->checkSecurityToken($this->request)) {
return $this->httpError(400);
}
$comment = $this->getComment();
if(($comment = $this->getComment()) && $comment->canEdit()) {
$comment->IsSpam = true;
$comment->Moderated = true;
$comment->write();
return ($this->request->isAjax()) ? $comment->renderWith('CommentsInterface_singlecomment') : $this->redirectBack();
}
return $this->httpError(404);
}
/**
* Marks a given {@link Comment} as ham (not spam).
*/
public function ham() {
if(!$this->checkSecurityToken($this->request)) {
return $this->httpError(400);
}
$comment = $this->getComment();
if(($comment = $this->getComment()) && $comment->canEdit()) {
$comment->IsSpam = false;
$comment->Moderated = true;
$comment->write();
return ($this->request->isAjax()) ? $comment->renderWith('CommentsInterface_singlecomment') : $this->redirectBack();
}
return $this->httpError(404);
}
/**
* Marks a given {@link Comment} as approved.
*/
public function approve() {
if(!$this->checkSecurityToken($this->request)) {
return $this->httpError(400);
}
$comment = $this->getComment();
if(($comment = $this->getComment()) && $comment->canEdit()) {
$comment->IsSpam = false;
$comment->Moderated = true;
$comment->write();
return ($this->request->isAjax()) ? $comment->renderWith('CommentsInterface_singlecomment') : $this->redirectBack();
}
return $this->httpError(404);
}
/**
* Returns the comment referenced in the URL (by ID). Permission checking
* should be done in the callee.
*
* @return Comment|false
*/
public function getComment() {
$id = isset($this->urlParams['ID']) ? $this->urlParams['ID'] : false;
if($id) {
$comment = DataObject::get_by_id('Comment', $id);
if($comment) {
return $comment;
}
}
return false;
}
/**
* Checks the security token given with the URL to prevent CSRF attacks
* against administrators allowing users to hijack comment moderation.
*
* @param SS_HTTPRequest
*
* @return boolean
*/
public function checkSecurityToken($req) {
$token = SecurityToken::inst();
return $token->checkRequest($req);
}
/**
* Post a comment form
*
* @return Form
*/
public function CommentsForm() {
$usePreview = Commenting::get_config_value($this->getBaseClass(), 'use_preview');
$member = Member::currentUser();
$fields = new FieldList(
$dataFields = new CompositeField(
TextField::create("Name", _t('CommentInterface.YOURNAME', 'Your name'))
->setCustomValidationMessage(_t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name'))
->setAttribute('data-message-required', _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name')),
EmailField::create("Email", _t('CommentingController.EMAILADDRESS', "Your email address (will not be published)"))
->setCustomValidationMessage(_t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address'))
->setAttribute('data-message-required', _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address'))
->setAttribute('data-message-email', _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid email address')),
TextField::create("URL", _t('CommentingController.WEBSITEURL', "Your website URL"))
->setAttribute('data-message-url', _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL')),
TextareaField::create("Comment", _t('CommentingController.COMMENTS', "Comments"))
->setCustomValidationMessage(_t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment'))
->setAttribute('data-message-required', _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment'))
),
HiddenField::create("ParentID"),
HiddenField::create("ReturnURL"),
HiddenField::create("BaseClass")
);
// Preview formatted comment. Makes most sense when shortcodes or
// limited HTML is allowed. Populated by JS/Ajax.
if($usePreview) {
$fields->insertAfter(
ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
->setAttribute('style', 'display: none'), // enable through JS
'Comment'
);
}
$dataFields->addExtraClass('data-fields');
// save actions
$actions = new FieldList(
new FormAction("doPostComment", _t('CommentInterface.POST', 'Post'))
);
if($usePreview) {
$actions->push(
FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
->addExtraClass('action-minor')
->setAttribute('style', 'display: none') // enable through JS
);
}
// required fields for server side
$required = new RequiredFields(array(
'Name',
'Email',
'Comment'
));
// create the comment form
$form = new Form($this, 'CommentsForm', $fields, $actions, $required);
// if the record exists load the extra required data
if($record = $this->getOwnerRecord()) {
$require_login = Commenting::get_config_value($this->getBaseClass(), 'require_login');
$permission = Commenting::get_config_value($this->getBaseClass(), 'required_permission');
if(($require_login || $permission) && $member) {
$fields = $form->Fields();
$fields->removeByName('Name');
$fields->removeByName('Email');
$fields->insertBefore(new ReadonlyField("NameView", _t('CommentInterface.YOURNAME', 'Your name'), $member->getName()), 'URL');
$fields->push(new HiddenField("Name", "", $member->getName()));
$fields->push(new HiddenField("Email", "", $member->Email));
$form->setFields($fields);
}
// we do not want to read a new URL when the form has already been submitted
// which in here, it hasn't been.
$url = (isset($_SERVER['REQUEST_URI'])) ? Director::protocolAndHost() . '' . $_SERVER['REQUEST_URI'] : false;
$form->loadDataFrom(array(
'ParentID' => $record->ID,
'ReturnURL' => $url,
'BaseClass' => $this->getBaseClass()
));
}
// Set it so the user gets redirected back down to the form upon form fail
$form->setRedirectToFormOnValidationError(true);
// load any data from the cookies
if($data = Cookie::get('CommentsForm_UserData')) {
$data = Convert::json2array($data);
$form->loadDataFrom(array(
"Name" => isset($data['Name']) ? $data['Name'] : '',
"URL" => isset($data['URL']) ? $data['URL'] : '',
"Email" => isset($data['Email']) ? $data['Email'] : ''
));
// allow previous value to fill if comment not stored in cookie (i.e. validation error)
$prevComment = Cookie::get('CommentsForm_Comment');
if($prevComment && $prevComment != ''){
$form->loadDataFrom(array("Comment" => $prevComment));
}
}
if($member) {
$form->loadDataFrom($member);
}
// hook to allow further extensions to alter the comments form
$this->extend('alterCommentForm', $form);
return $form;
}
/**
* Process which creates a {@link Comment} once a user submits a comment from this form.
*
* @param array $data
* @param Form $form
*/
public function doPostComment($data, $form) {
$class = (isset($data['BaseClass'])) ? $data['BaseClass'] : $this->getBaseClass();
$usePreview = Commenting::get_config_value($class, 'use_preview');
$isPreview = ($usePreview && isset($data['IsPreview']) && $data['IsPreview']);
// if no class then we cannot work out what controller or model they
// are on so throw an error
if(!$class) user_error("No OwnerClass set on CommentingController.", E_USER_ERROR);
// cache users data
Cookie::set("CommentsForm_UserData", Convert::raw2json($data));
Cookie::set("CommentsForm_Comment", $data['Comment']);
// extend hook to allow extensions. Also see onAfterPostComment
$this->extend('onBeforePostComment', $form);
// If commenting can only be done by logged in users, make sure the user is logged in
$member = Member::currentUser();
if(Commenting::can_member_post($class) && $member) {
$form->Fields()->push(new HiddenField("AuthorID", "Author ID", $member->ID));
}
if(!Commenting::can_member_post($class)) {
echo _t('CommentingController.PERMISSIONFAILURE', "You're not able to post comments to this page. Please ensure you are logged in and have an appropriate permission level.");
return;
}
// is moderation turned on
$moderated = Commenting::get_config_value($class, 'require_moderation');
if(!$moderated){
$moderated_nonmembers = Commenting::get_config_value($class, 'require_moderation_nonmembers');
$moderated = $moderated_nonmembers ? !Member::currentUser() : false;
}
// we want to show a notification if comments are moderated
if ($moderated) {
Session::set('CommentsModerated', 1);
}
$comment = new Comment();
$form->saveInto($comment);
$comment->AllowHtml = Commenting::get_config_value($class, 'html_allowed');
$comment->Moderated = ($moderated) ? false : true;
// Save into DB, or call pre-save hooks to give accurate preview
if($isPreview) {
$comment->extend('onBeforeWrite', $dummy);
} else {
$comment->write();
// extend hook to allow extensions. Also see onBeforePostComment
$this->extend('onAfterPostComment', $comment);
}
// clear the users comment since it passed validation
Cookie::set('CommentsForm_Comment', false);
$holder = Commenting::get_config_value($comment->BaseClass, 'comments_holder_id');
$hash = ($moderated) ? $holder : $comment->Permalink();
$url = (isset($data['ReturnURL'])) ? $data['ReturnURL'] : false;
return ($url) ? $this->redirect($url .'#'. $hash) : $this->redirectBack();
}
public function doPreviewComment($data, $form) {
$data['IsPreview'] = 1;
return $this->doPostComment($data, $form);
}
}

View File

@ -1,420 +0,0 @@
<?php
/**
* Represents a single comment object.
*
* @package comments
*/
class Comment extends DataObject {
private static $db = array(
"Name" => "Varchar(200)",
"Comment" => "Text",
"Email" => "Varchar(200)",
"URL" => "Varchar(255)",
"BaseClass" => "Varchar(200)",
"Moderated" => "Boolean",
"IsSpam" => "Boolean",
"ParentID" => "Int",
'AllowHtml' => "Boolean"
);
private static $has_one = array(
"Author" => "Member"
);
private static $default_sort = '"Created" DESC';
private static $has_many = array();
private static $many_many = array();
private static $defaults = array(
"Moderated" => 1,
"IsSpam" => 0
);
private static $casting = array(
'AuthorName' => 'Varchar',
'RSSName' => 'Varchar'
);
private static $searchable_fields = array(
'Name',
'Email',
'Comment',
'Created',
'BaseClass',
);
private static $summary_fields = array(
'Name' => 'Submitted By',
'Email' => 'Email',
'Comment' => 'Comment',
'Created' => 'Date Posted',
'ParentTitle' => 'Parent',
'IsSpam' => 'Is Spam'
);
public function onBeforeWrite() {
parent::onBeforeWrite();
// Sanitize HTML, because its expected to be passed to the template unescaped later
if($this->AllowHtml) {
$this->Comment = $this->purifyHtml($this->Comment);
}
}
/**
* Migrates the old {@link PageComment} objects to {@link Comment}
*/
public function requireDefaultRecords() {
parent::requireDefaultRecords();
if(DB::getConn()->hasTable('PageComment')) {
$comments = DB::query("SELECT * FROM \"PageComment\"");
if($comments) {
while($pageComment = $comments->nextRecord()) {
// create a new comment from the older page comment
$comment = new Comment();
$comment->update($pageComment);
// set the variables which have changed
$comment->BaseClass = 'SiteTree';
$comment->URL = (isset($pageComment['CommenterURL'])) ? $pageComment['CommenterURL'] : "";
if((int)$pageComment['NeedsModeration'] == 0) $comment->Moderated = true;
$comment->write();
}
}
DB::alteration_message("Migrated PageComment to Comment","changed");
DB::getConn()->dontRequireTable('PageComment');
}
}
/**
* Return a link to this comment
*
* @return string link to this comment.
*/
public function Link($action = "") {
if($parent = $this->getParent()){
return $parent->Link($action) . '#' . $this->Permalink();
}
}
/**
* Returns the permalink for this {@link Comment}. Inserted into
* the ID tag of the comment
*
* @return string
*/
public function Permalink() {
$prefix = Commenting::get_config_value($this->BaseClass, 'comment_permalink_prefix');
return $prefix . $this->ID;
}
/**
* Translate the form field labels for the CMS administration
*
* @param boolean $includerelations
*/
public function fieldLabels($includerelations = true) {
$labels = parent::fieldLabels($includerelations);
$labels['Name'] = _t('Comment.NAME', 'Author Name');
$labels['Comment'] = _t('Comment.COMMENT', 'Comment');
$labels['Email'] = _t('Comment.EMAIL', 'Email');
$labels['URL'] = _t('Comment.URL', 'URL');
$labels['IsSpam'] = _t('Comment.ISSPAM', 'Spam?');
$labels['Moderated'] = _t('Comment.MODERATED', 'Moderated?');
$labels['ParentTitle'] = _t('Comment.PARENTTITLE', 'Parent');
$labels['Created'] = _t('Comment.CREATED', 'Date posted');
return $labels;
}
/**
* Returns the parent {@link DataObject} this comment is attached too
*
* @return DataObject
*/
public function getParent() {
if(!$this->BaseClass) {
$this->BaseClass = "SiteTree";
}
return ($this->ParentID) ? DataObject::get_by_id($this->BaseClass, $this->ParentID) : null;
}
/**
* Returns a string to help identify the parent of the comment
*
* @return string
*/
public function getParentTitle() {
if($parent = $this->getParent()){
return ($parent && $parent->Title) ? $parent->Title : $parent->ClassName . " #" . $parent->ID;
}
}
/**
* Comment-parent classnames obviousely vary, return the parent classname
*
* @return string
*/
public function getParentClassName() {
$default = 'SiteTree';
if(!$this->BaseClass) {
return $default;
}
return $this->BaseClass;
}
/**
* Return the content for this comment escaped depending on the Html state.
*
* @return HTMLText
*/
public function getEscapedComment() {
$comment = $this->dbObject('Comment');
if ($comment->exists()) {
if ($this->AllowHtml) {
return DBField::create_field('HTMLText', nl2br($comment->RAW()));
} else {
return DBField::create_field('HTMLText', sprintf("<p>%s</p>", nl2br($comment->XML())));
}
}
return $comment;
}
/**
* Return whether this comment is a preview (has not been written to the db)
*
* @return boolean
*/
public function isPreview() {
return ($this->ID < 1);
}
/**
* @todo needs to compare to the new {@link Commenting} configuration API
*
* @return Boolean
*/
public function canCreate($member = null) {
return false;
}
/**
* Checks for association with a page, and {@link SiteTree->ProvidePermission}
* flag being set to true.
*
* @param Member $member
* @return Boolean
*/
public function canView($member = null) {
if(!$member) $member = Member::currentUser();
// Standard mechanism for accepting permission changes from decorators
$extended = $this->extendedCan('canView', $member);
if($extended !== null) return $extended;
$page = $this->getParent();
$admin = (bool) Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin');
return (($page && $page->ProvideComments && $page->canView($member)) || $admin);
}
/**
* Checks for "CMS_ACCESS_CommentAdmin" permission codes and
* {@link canView()}.
*
* @param Member $member
* @return Boolean
*/
public function canEdit($member = null) {
if(!$member) $member = Member::currentUser();
// Standard mechanism for accepting permission changes from decorators
$extended = $this->extendedCan('canEdit', $member);
if($extended !== null) return $extended;
if(!$this->canView($member)) return false;
return (bool)Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin');
}
/**
* Checks for "CMS_ACCESS_CommentAdmin" permission codes and
* {@link canEdit()}.
*
* @param Member $member
* @return Boolean
*/
public function canDelete($member = null) {
if(!$member) $member = Member::currentUser();
// Standard mechanism for accepting permission changes from decorators
$extended = $this->extendedCan('canDelete', $member);
if($extended !== null) return $extended;
return $this->canEdit($member);
}
/**
* Return the authors name for the comment
*
* @return string
*/
public function getAuthorName() {
if($this->Name) {
return $this->Name;
} else if($this->Author()) {
return $this->Author()->getName();
}
}
/**
* @return string
*/
public function DeleteLink() {
if($this->canDelete()) {
$token = SecurityToken::inst();
return DBField::create_field("Varchar", Director::absoluteURL($token->addToUrl(sprintf(
"CommentingController/delete/%s", (int) $this->ID
))));
}
}
/**
* @return string
*/
public function SpamLink() {
if($this->canEdit() && !$this->IsSpam) {
$token = SecurityToken::inst();
return DBField::create_field("Varchar", Director::absoluteURL($token->addToUrl(sprintf(
"CommentingController/spam/%s", (int) $this->ID
))));
}
}
/**
* @return string
*/
public function HamLink() {
if($this->canEdit() && $this->IsSpam) {
$token = SecurityToken::inst();
return DBField::create_field("Varchar", Director::absoluteURL($token->addToUrl(sprintf(
"CommentingController/ham/%s", (int) $this->ID
))));
}
}
/**
* @return string
*/
public function ApproveLink() {
if($this->canEdit() && !$this->Moderated) {
$token = SecurityToken::inst();
return DBField::create_field("Varchar", Director::absoluteURL($token->addToUrl(sprintf(
"CommentingController/approve/%s", (int) $this->ID
))));
}
}
/**
* @return string
*/
public function SpamClass() {
if($this->IsSpam) {
return 'spam';
} else if(!$this->Moderated) {
return 'unmoderated';
} else {
return 'notspam';
}
}
/**
* @return string
*/
public function getTitle() {
$title = sprintf(_t('Comment.COMMENTBY', "Comment by %s", 'Name'), $this->getAuthorName());
if($parent = $this->getParent()) {
if($parent->Title) {
$title .= sprintf(" %s %s", _t('Comment.ON', 'on'), $parent->Title);
}
}
return $title;
}
/*
* Modify the default fields shown to the user
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$parent = $this->getParent()->ID;
$hidden = array('ParentID', 'AuthorID', 'BaseClass', 'AllowHtml');
foreach($hidden as $private) {
$fields->removeByName($private);
}
return $fields;
}
/**
* @param String $dirtyHtml
* @return String
*/
public function purifyHtml($dirtyHtml) {
$purifier = $this->getHtmlPurifierService();
return $purifier->purify($dirtyHtml);
}
/**
* @return HTMLPurifier (or anything with a "purify()" method)
*/
public function getHtmlPurifierService() {
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.AllowedElements',
Commenting::get_config_value($this->BaseClass, 'html_allowed_elements')
);
$config->set('AutoFormat.AutoParagraph', true);
$config->set('AutoFormat.Linkify', true);
$config->set('URI.DisableExternalResources', true);
return new HTMLPurifier($config);
}
/*
Calcualate the gravatar link from the email address
*/
public function Gravatar() {
$gravatar = '';
$use_gravatar = Commenting::get_config_value($this->BaseClass, 'use_gravatar');
if ($use_gravatar) {
$gravatar = "http://www.gravatar.com/avatar/" . md5( strtolower(trim($this->Email)));
$gravatarsize = Commenting::get_config_value($this->BaseClass, 'gravatar_size');
$gravatardefault = Commenting::get_config_value($this->BaseClass, 'gravatar_default');
$gravatarrating = Commenting::get_config_value($this->BaseClass, 'gravatar_rating');
$gravatar.= "?s=".$gravatarsize."&d=".$gravatardefault."&r=".$gravatarrating;
}
return $gravatar;
}
}

View File

@ -1,157 +0,0 @@
<?php
/**
* Extension to {@link DataObject} to enable tracking comments.
*
* @package comments
*/
class CommentsExtension extends DataExtension {
public static function get_extra_config($class, $extension, $args = null) {
$config = array();
// if it is attached to the SiteTree then we need to add ProvideComments
if(is_subclass_of($class, 'SiteTree') || $class == 'SiteTree') {
$config['db'] = array('ProvideComments' => 'Boolean');
}
return $config;
}
/**
* If this extension is applied to a {@link SiteTree} record then
* append a Provide Comments checkbox to allow authors to trigger
* whether or not to display comments
*
* @todo Allow customization of other {@link Commenting} configuration
*
* @param FieldSet
*/
public function updateSettingsFields(FieldList $fields) {
if($this->attachedToSiteTree()) {
$fields->addFieldToTab('Root.Settings',
new CheckboxField('ProvideComments', _t('Comment.ALLOWCOMMENTS', 'Allow Comments'))
);
}
}
/**
* Returns a list of all the comments attached to this record.
*
* @return PaginatedList
*/
public function getComments() {
$order = Commenting::get_config_value($this->ownerBaseClass, 'order_comments_by');
$list = Comment::get()->filter(array(
'ParentID' => $this->owner->ID,
'BaseClass' => $this->ownerBaseClass
))->sort($order);
// Filter content for unauthorised users
if (!($member = Member::currentUser()) || !Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
// Filter unmoderated comments for non-administrators if moderation is enabled
if (Commenting::get_config_value($this->ownerBaseClass, 'require_moderation') || Commenting::get_config_value($this->ownerBaseClass, 'require_moderation_nonmembers')) {
$list = $list->filter(array(
'Moderated' => 1,
'IsSpam' => 0
));
} else {
// Filter spam comments for non-administrators if auto-moderated
$list = $list->filter('IsSpam', 0);
}
}
$list = new PaginatedList($list);
$list->setPageLength(Commenting::get_config_value(
$this->ownerBaseClass, 'comments_per_page'
));
$controller = Controller::curr();
$list->setPageStart($controller->request->getVar("commentsstart". $this->owner->ID));
$list->setPaginationGetVar("commentsstart". $this->owner->ID);
return $list;
}
/**
* Comments interface for the front end. Includes the CommentAddForm and the composition
* of the comments display.
*
* To customize the html see templates/CommentInterface.ss or extend this function with
* your own extension.
*
* @todo Cleanup the passing of all this configuration based functionality
*
* @see docs/en/Extending
*/
public function CommentsForm() {
if(Commenting::has_commenting($this->ownerBaseClass) && Commenting::get_config_value($this->ownerBaseClass, 'include_js')) {
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/lib/jquery.form.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.pack.js');
Requirements::javascript('comments/javascript/CommentsInterface.js');
}
$interface = new SSViewer('CommentsInterface');
// detect whether we comments are enabled. By default if $CommentsForm is included
// on a {@link DataObject} then it is enabled, however {@link SiteTree} objects can
// trigger comments on / off via ProvideComments
$enabled = (!$this->attachedToSiteTree() || $this->owner->ProvideComments) ? true : false;
// do not include the comments on pages which don't have id's such as security pages
if($this->owner->ID < 0) return false;
$controller = new CommentingController();
$controller->setOwnerRecord($this->owner);
$controller->setBaseClass($this->ownerBaseClass);
$controller->setOwnerController(Controller::curr());
$moderatedSubmitted = Session::get('CommentsModerated');
Session::clear('CommentsModerated');
$form = ($enabled) ? $controller->CommentsForm() : false;
// a little bit all over the show but to ensure a slightly easier upgrade for users
// return back the same variables as previously done in comments
return $interface->process(new ArrayData(array(
'CommentHolderID' => Commenting::get_config_value($this->ownerBaseClass, 'comments_holder_id'),
'PostingRequiresPermission' => Commenting::get_config_value($this->ownerBaseClass, 'required_permission'),
'CanPost' => Commenting::can_member_post($this->ownerBaseClass),
'RssLink' => "CommentingController/rss",
'RssLinkPage' => "CommentingController/rss/". $this->ownerBaseClass . '/'.$this->owner->ID,
'CommentsEnabled' => $enabled,
'Parent' => $this->owner,
'AddCommentForm' => $form,
'ModeratedSubmitted' => $moderatedSubmitted,
'Comments' => $this->getComments()
)));
}
/**
* Returns whether this extension instance is attached to a {@link SiteTree} object
*
* @return bool
*/
public function attachedToSiteTree() {
$class = $this->ownerBaseClass;
return (is_subclass_of($class, 'SiteTree')) || ($class == 'SiteTree');
}
/**
* @deprecated 1.0 Please use {@link CommentsExtension->CommentsForm()}
*/
public function PageComments() {
// This method is very commonly used, don't throw a warning just yet
//user_error('$PageComments is deprecated. Please use $CommentsForm', E_USER_WARNING);
return $this->CommentsForm();
}
}

View File

@ -1,17 +1,49 @@
{
"name": "silverstripe/comments",
"description": "This module provides commenting functionality for Pages and other DataObjects on your SilverStripe site.",
"type": "silverstripe-module",
"keywords": ["silverstripe", "comments"],
"authors": [{
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}],
"require": {
"silverstripe/framework": "~3.1",
"colymba/gridfield-bulk-editing-tools": "2.0.2"
},
"suggest": {
"ezyang/htmlpurifier": "4.*"
}
}
"name": "silverstripe/comments",
"description": "This module provides commenting functionality for Pages and other DataObjects on your SilverStripe site.",
"type": "silverstripe-vendormodule",
"keywords": [
"silverstripe",
"comments"
],
"authors": [
{
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}
],
"require": {
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10",
"colymba/gridfield-bulk-editing-tools": "^3.0.0-beta4"
},
"suggest": {
"ezyang/htmlpurifier": "Standards compliant HTML filter written in PHP",
"silverstripe/cms": "The SilverStripe Content Management System"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0",
"ezyang/htmlpurifier": "^4.14"
},
"extra": {
"expose": [
"client/dist",
"client/lang",
"css"
]
},
"scripts": {
"lint": "vendor/bin/phpcs src/ tests/",
"fix": "vendor/bin/phpcbf src/ tests/"
},
"autoload": {
"psr-4": {
"SilverStripe\\Comments\\": "src/",
"SilverStripe\\Comments\\Tests\\": "tests/"
}
},
"license": "BSD-3-Clause",
"minimum-stability": "dev",
"prefer-stable": true
}

1
css/comments.css Normal file
View File

@ -0,0 +1 @@
/** Deprecated. Remove in 4.0 release. */

View File

@ -2,97 +2,159 @@
## Overview
The module provides a number of built in configuration settings below are the
The module provides a number of built in configuration settings below are the
default settings
// mysite/_config.php
Commenting::add('Foo', array(
'require_login' => false, // boolean, whether a user needs to login
'required_permission' => false, // required permission to comment (or array of permissions)
'include_js' => true, // Enhance operation by ajax behaviour on moderation links
'show_comments_when_disabled' => false, // when comments are disabled should we show older comments (if available)
'order_comments_by' => "\"Created\" DESC",
'comments_per_page' => 10,
'comments_holder_id' => "comments-holder", // id for the comments holder
'comment_permalink_prefix' => "comment-", // id prefix for each comment. If needed make this different
'require_moderation' => false,
'html_allowed' => false, // allow for sanitized HTML in comments
'html_allowed_elements' => array('a', 'img', 'i', 'b'),
'use_preview' => false, // preview formatted comment (when allowing HTML). Requires include_js=true
'use_gravatar' => false,
'gravatar_size' => 80
));
If you want to customize any of the configuration options after you have added
In order to add commenting to your site, the minimum amount of work necessary is to add the `CommentsExtension` to
the base class for the object which holds comments.
```yaml
SilverStripe\CMS\Model\SiteTree:
extensions:
- SilverStripe\Comments\Extensions\CommentsExtension
```
## Configuration
In order to configure options for any class you should assign the specific option a value under the 'comments'
config of the specified class.
```yaml
SilverStripe\CMS\Model\SiteTree:
extensions:
- SilverStripe\Comments\Extensions\CommentsExtension
comments:
enabled: true # Enables commenting to be disabled for a specific class (or subclass of a parent with commenting enabled)
enabled_cms: false # The 'enabled' option will be set via the CMS instead of config
require_login: false # boolean, whether a user needs to login
require_login_cms: false # The 'require_login' option will be set via the CMS instead of config
required_permission: false # required permission to comment (or array of permissions)
include_js: true # Enhance operation by ajax behaviour on moderation links
use_gravatar: false # set to true to show gravatar icons,
gravatar_size: 80 # size of gravatar in pixels. This is the same as the standard default
gravatar_default: 'identicon' # theme for 'not found' gravatar (see http://gravatar.com/site/implement/images/)
gravatar_rating: 'g' # gravatar rating. This is the same as the standard default
show_comments_when_disabled: false # when comments are disabled should we show older comments (if available)
order_comments_by: '"Created" DESC'
comments_per_page: 10
comments_holder_id: 'comments-holder' # id for the comments holder
comment_permalink_prefix: 'comment-' # id prefix for each comment. If needed make this different
require_moderation: false
require_moderation_nonmembers: false # requires moderation for comments posted by non-members. 'require_moderation' overrides this if set.
require_moderation_cms: false # If true, ignore above values and configure moderation requirements via the CMS only
frontend_moderation: false # Display unmoderated comments in the frontend, if the user can moderate them.
frontend_spam: false # Display spam comments in the frontend, if the user can moderate them.
html_allowed: false # allow for sanitized HTML in comments
html_allowed_elements:
- a
- img
- i
- b
use_preview: false # preview formatted comment (when allowing HTML). Requires include_js=true
nested_comments: false # If true comments can be replied to up to nested_depth levels
nested_depth: 2 # The maximum depth of the comment hierarchy for comment reply purposes
```
Enabling any of the *_cms options will instead allow these options to be configured under the settings tab
of each page in the CMS.
If you want to customize any of the configuration options after you have added
the extension (or on the built-in SiteTree commenting) use `set_config_value`
// mysite/_config.php - Sets require_login to true for all pages
Commenting::set_config_value('SiteTree', 'require_login', true);
// mysite/_config.php - Returns the setting
Commenting::get_config_value('SiteTree', 'require_login');
```yaml
# Set the default option for pages to require login
SilverStripe\CMS\Model\SiteTree:
comments:
require_login: true
```
```php
use SilverStripe\CMS\Model\SiteTree;
// [...]
// Get the setting
$loginRequired = singleton(SiteTree::class)->getCommentsOption('require_login');
```
## HTML Comments
Comments can be configured to contain a restricted set of HTML tags through the
`html_allowed` and `html_allowed_elements` settings. Raw HTML is hardly user
friendly, but combined with a rich-text editor of your own choosing it can
Comments can be configured to contain a restricted set of HTML tags through the
`html_allowed` and `html_allowed_elements` settings. Raw HTML is hardly user
friendly, but combined with a rich-text editor of your own choosing it can
allow rich comment formatting.
In order to use this feature, you need to install the
[HTMLPurifier](http://htmlpurifier.org/) library. The easiest way to do this is
[HTMLPurifier](http://htmlpurifier.org/) library. The easiest way to do this is
through [Composer](http://getcomposer.org).
{
"require": {"ezyang/htmlpurifier": "4.*"}
}
```json
{
"require": {"ezyang/htmlpurifier": "^4.8"}
}
```
**Important**: Rendering user-provided HTML on your website always risks
exposing your users to cross-site scripting (XSS) attacks, if the HTML isn't
**Important**: Rendering user-provided HTML on your website always risks
exposing your users to cross-site scripting (XSS) attacks, if the HTML isn't
properly sanitized. Don't allow tags like `<script>` or arbitrary attributes.
## Gravatars
Gravatars can be turned on by adding this to your mysite/_config.php file
Gravatars can be turned on by adding this to your mysite/_config/config.yml file
Commenting::set_config_value('SiteTree', 'use_gravatar', true);
```yaml
SilverStripe\CMS\Model\SiteTree:
comments:
use_gravatar: true
````
The default size is 80 pixels, as per the gravatar site if the 's' parameter is
omitted. To change this add the following (again to mysite/_config.php):
The default size is 80 pixels, as per the gravatar site if the 's' parameter is
omitted. To change this add the following (again to mysite/_config/config.yml):
Commenting::set_config_value('SiteTree', 'gravatar_size', 40);
```yaml
SilverStripe\CMS\Model\SiteTree:
comments:
gravatar_size: 40
```
If the email address used to comment does not have a gravatar, it is possible
to configure the default image shown. Valid values can be found at
http://gravatar.com/site/implement/images/, and at the time of writing are the
If the email address used to comment does not have a gravatar, it is possible
to configure the default image shown. Valid values can be found at
http://gravatar.com/site/implement/images/, and at the time of writing are the
following:
* 404: do not load any image if none is associated with the email hash, instead
* 404: do not load any image if none is associated with the email hash, instead
return an HTTP 404 (File Not Found) response.
* mm: (mystery-man) a simple, cartoon-style silhouetted outline of a person
* mm: (mystery-man) a simple, cartoon-style silhouetted outline of a person
(does not vary by email hash).
* identicon: a geometric pattern based on an email hash
* monsterid: a generated 'monster' with different colors, faces, etc
* wavatar: generated faces with differing features and backgrounds
* retro: awesome generated, 8-bit arcade-style pixelated faces
* blank: a transparent PNG image (border added to HTML below for demonstration
* blank: a transparent PNG image (border added to HTML below for demonstration
purposes)
To change the default image style, add the following to mysite/_config.php
To change the default image style, add the following to mysite/_config/config.yml
Commenting::set_config_value('SiteTree', 'gravatar_default', 'retro');
```yaml
SilverStripe\CMS\Model\SiteTree:
comments:
gravatar_default: 'retro'
```
The rating of the image can be changed by adding a line similar to this to
mysite/_config.php
The rating of the image can be changed by adding a line similar to this to
mysite/_config/config.yml
Commenting::set_config_value('SiteTree', 'gravatar_rating', 'r');
```yaml
SilverStripe\CMS\Model\SiteTree:
comments:
gravatar_rating: 'r'
```
Vald values for rating are as follows:
Valid values for rating are as follows:
* g: suitable for display on all websites with any audience type.
* pg: may contain rude gestures, provocatively dressed individuals, the lesser
* pg: may contain rude gestures, provocatively dressed individuals, the lesser
swear words, or mild violence.
* r: may contain such things as harsh profanity, intense violence, nudity, or
* r: may contain such things as harsh profanity, intense violence, nudity, or
hard drug use.
* x: may contain hardcore sexual imagery or extremely disturbing violence.

View File

@ -1,63 +1,25 @@
# Installation
## Composer
Edit your project-wide composer.json file as follows; in the "require" block add:
"silverstripe/comments": "*"
Then in the root of your project run:
#> composer update silverstripe/comments
```sh
composer require silverstripe/comments`
```
## Web
To begin the installation first download the module online. You can find the version
you require for your SilverStripe installation on the [silverstripe.org](http://www.silverstripe.org) website.
After you have finished downloading the file, extract the downloaded file to your site's root
folder and ensure the name of the module is `comments`.
## All
Run a database rebuild by visiting *http://yoursite.com/dev/build*. This will add the required database
columns and tables for the module to function.
If you previously had SilverStripe version 2.4 installed then you'll also need to run the migration script
provided. More information on this is provided in the next section.
Then run a database rebuild by visiting `dev/build`. This will add the required database columns and tables for the module to function, and refresh the configuration manifest.
## Enabling Commenting
Out of the box the module adds commenting support to all pages on your site. This functionality can be
turned on and off on a per page basis in the CMS under the `Behaviour` tab for a given page. Once the `Allow Comments`
checkbox is ticked, republish and view the webpage.
Ensure that your template file (usually themes/yourtheme/templates/Layout/Page.ss) has the `$CommentsForm` variable
where you want comments to appear.
To enable commenting on other objects (such as your own subclasses of `DataObject`) add the following
to your `mysite/_config.php` file.
// Adds commenting to the class SiteTree (all Pages)
Commenting::add('SiteTree');
You can also pass configuration options to the add function to customize commenting on that object. Again
in your `mysite/_config.php` file replace the previous line with the following
// Adds commenting to the class SiteTree (all Pages) but require the user to login and comments
// are moderated
Commenting::add('Foo', array(
'require_moderation' => true,
'require_login' => true
));
Out of the box the module adds commenting support to all pages on your site. This functionality can be turned on and off on a per page basis in the CMS under the `Behaviour` tab for a given page. Once the `Allow comments` checkbox is ticked, republish and view the webpage.
For more configuration options see [Configuration](Configuration.md).
## Upgrading
## Displaying Comments
### Migrating from version 2.* SilverStripe installations
To display form and comments simply add
This module replaces the built-in commenting system available in versions up to SilverStripe 2.4. To migrate from
that you'll need to run `dev/build` after installing the module.
```sh
{$CommentsForm}
```
You can do this via sake (`sake dev/build`) or via a web browser by visiting `http://yoursite.com/dev/build`
to your `.ss` file.

View File

@ -1,178 +0,0 @@
/**
* @package comments
*/
(function($) {
$(document).ready(function () {
var container = $('.comments-holder-container'),
commentsHolder = $('.comments-holder'),
commentsList = $('.comments-list', commentsHolder),
pagination = $('.comments-pagination'),
noCommentsYet = $('.no-comments-yet', commentsHolder),
form = $('form', container),
previewEl = form.find('#PreviewComment');
/**
* Init
*/
previewEl.hide();
$(':submit[name=action_doPreviewComment]').show();
/**
* Validate
*/
form.validate({
invalidHandler : function(form, validator){
$('html, body').animate({
scrollTop: $(validator.errorList[0].element).offset().top - 30
}, 200);
},
showErrors: function(errorMap, errorList) {
this.defaultShowErrors();
// hack to add the extra classes we need to the validation message elements
form.find('span.error').addClass('message required');
},
errorElement: "span",
errorClass: "error",
ignore: '.hidden',
rules: {
Name : {
required : true
},
Email : {
required : true,
email : true
},
Comment: {
required : true
},
URL: {
url : true
}
},
messages: {
Name : {
required : form.find('[name="Name"]').data('message-required')
},
Email : {
required : form.find('[name="Email"]').data('message-required'),
email : form.find('[name="Email"]').data('message-email')
},
Comment: {
required : form.find('[name="Comment"]').data('message-required')
},
URL: {
url : form.find('[name="Comment"]').data('message-url')
}
}
});
form.submit(function (e) {
// trigger validation
if(!form.validate().valid()) return false;
});
/**
* Preview comment by fetching it from the server via ajax.
*/
$(':submit[name=action_doPreviewComment]', form).click(function(e) {
e.preventDefault();
if(!form.validate().valid()) {
return false;
}
previewEl.show().addClass('loading').find('.middleColumn').html(' ');
form.ajaxSubmit({
success: function(response) {
var responseEl = $(response);
if(responseEl.is('form')) {
// Validation failed, renders form instead of single comment
form.find(".data-fields").replaceWith(responseEl.find(".data-fields"));
} else {
// Default behaviour
previewEl.removeClass('loading').find('.middleColumn').html(responseEl);
}
},
data: {'action_doPreviewComment': 1}
});
});
/**
* Hide outdated preview on form changes
*/
$(':input', form).on('change keydown', function() {
previewEl.removeClass('loading').hide();
});
/**
* Clicking one of the metalinks performs the operation via ajax
* this inclues the spam and approve links
*/
commentsList.on('click', '.action-links a', function(e) {
var link = $(this);
var comment = link.parents('.comment:first');
$.ajax({
url: $(this).attr('href'),
cache: false,
success: function(html){
if(link.hasClass('ham')) {
// comment has been marked as not spam
comment.html(html);
comment.removeClass('spam').hide().fadeIn();
}
else if(link.hasClass('approve')) {
// comment has been approved
comment.html(html);
comment.removeClass('unmoderated').hide().fadeIn();
}
else if(link.hasClass('delete')) {
comment.fadeOut(1000, function() {
comment.remove();
if(commentsList.children().length == 0) {
noCommentsYet.show();
}
});
}
else if(link.hasClass('spam')) {
comment.html(html).addClass('spam').hide().fadeIn();
}
},
failure: function(html) {
alert(html)
}
});
e.preventDefault();
});
/**
* Ajax pagination
*/
pagination.find('a').on('click', function(){
commentsList.addClass('loading');
$.ajax({
url: $(this).attr('href'),
cache: false,
success: function(html){
html = $(html);
commentsList.hide().html(html.find('.comments-list:first').html()).fadeIn();
pagination.hide().html(html.find('.comments-pagination:first').html()).fadeIn();
commentsList.removeClass('loading');
$('html, body').animate({
scrollTop: commentsList.offset().top - 30
}, 200);
},
failure: function(html) {
alert('Error loading comments');
}
});
return false;
});
});
})(jQuery);

22
lang/ar.yml Normal file
View File

@ -0,0 +1,22 @@
ar:
CommentInterface:
PREVIEW: معاينة
PREVIEWLABEL: معاينة
CommentsInterface_ss:
COMMENTS: التعليقات
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: التعليقات
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: التعليقات
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: التعليقات
COMMENTSTABSET: التعليقات
SilverStripe\Comments\Model\Comment:
COMMENT: تعليق
EMAIL: 'البريد الإلكتروني'
'ON': في
OPTIONS: الخيارات
PARENTTITLE: والد
PLURALNAME: التعليقات
SINGULARNAME: تعليق
URL: الرابط

18
lang/bg.yml Normal file
View File

@ -0,0 +1,18 @@
bg:
CommentInterface:
PREVIEW: Преглед
PREVIEWLABEL: Преглед
CommentsInterface_ss:
COMMENTS: Коментари
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Коментари
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Коментари
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Коментари
COMMENTSTABSET: Коментари
SilverStripe\Comments\Model\Comment:
EMAIL: Еmail
'ON': на
PLURALNAME: Коментари
URL: URL

19
lang/cs.yml Normal file
View File

@ -0,0 +1,19 @@
cs:
CommentInterface:
POST: Post
PREVIEW: Náhled
PREVIEWLABEL: Náhled
CommentsInterface_ss:
COMMENTS: Komentáře
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentáře
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentáře
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentáře
COMMENTSTABSET: Komentáře
SilverStripe\Comments\Model\Comment:
EMAIL: E-mail
'ON': v
PLURALNAME: Komentáře
URL: URL

26
lang/cs_CZ.yml Normal file
View File

@ -0,0 +1,26 @@
cs_CZ:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Prosím vložte Váš komentář'
COMMENT_MESSAGE_URL: 'Prosím zadejte platnou URL adresu'
EMAILADDRESS_MESSAGE_EMAIL: 'Prosím zadejte platnou emailovou adresu'
EMAILADDRESS_MESSAGE_REQUIRED: 'Prosím zadejte Vaší emailovou adresu'
PREVIEW: Náhled
PREVIEWLABEL: Náhled
YOURNAME: 'Vaše jméno'
YOURNAME_MESSAGE_REQUIRED: 'Prosím zadejte Vaše jméno'
Comments:
COMMENTSREQUIRELOGIN: 'Vyžadováno přihlášení pro komentování'
CommentsAdmin:
NewComments: Nový
SpamComments: Spam
CommentsInterface_singlecomment_ss:
ISNTSPAM: 'Není spam'
SilverStripe\Comments\Admin\CommentAdmin:
NewComments: Nový
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
SPAM: Spam

18
lang/da.yml Normal file
View File

@ -0,0 +1,18 @@
da:
CommentInterface:
PREVIEW: Forhåndsvisning
PREVIEWLABEL: Forhåndsvisning
CommentsInterface_ss:
COMMENTS: Kommentarer
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Kommentarer
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentarer
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Kommentarer
COMMENTSTABSET: Kommentarer
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': 'd. '
PLURALNAME: Kommentarer
URL: URL

View File

@ -1,49 +1,88 @@
de:
Comment:
ALLOWCOMMENTS: 'Kommentare Erlauben'
COMMENT: Kommentar
COMMENTBY: 'Kommentar von ''%s'''
ISSPAM: 'Spam?'
MODERATED: 'Moderiert?'
NAME: 'Autor Name'
ON: on
PLURALNAME: Kommentare
SINGULARNAME: Kommentar
CommentAdmin:
Moderated: Moderiert
NeedsModeration: 'Benötigt Moderation'
MENUTITLE: Kommentare
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Bitte geben Sie Ihren Kommentar ein'
COMMENT_MESSAGE_URL: 'Bitte geben Sie eine gültige URL ein'
EMAILADDRESS_MESSAGE_EMAIL: 'Bitte geben Sie eine gültige E-Mail-Adresse ein'
EMAILADDRESS_MESSAGE_REQUIRED: 'Bitte geben Sie Ihre E-Mail-Adresse ein'
POST: Absenden
YOURNAME: 'Name'
CommentingController:
PREVIEW: Vorschau
PREVIEWLABEL: Vorschau
YOURNAME: Name
YOURNAME_MESSAGE_REQUIRED: 'Bitte geben Sie Ihren Namen ein'
Comments:
COMMENTSREQUIRELOGIN: 'Einloggen, um zu kommentieren'
CommentsAdmin:
Comments: Genehmigt
NewComments: Neu
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Ihr Kommentar wurde gespeichert und wird durch einen Moderator freigeschaltet.'
CommentsInterface_singlecomment_ss:
REPLYTO: 'Antworten an'
CommentsInterface_ss:
AWAITINGMODERATION: 'Ihr Kommentar wurde gespeichert und wird durch einen Moderator freigeschaltet.'
COMMENTLOGINERROR: 'Sie können diese Seite nur kommentieren, wenn Sie eingeloggt sind'
COMMENTPERMISSIONERROR: 'und die nötige Berechtigung besitzen'
COMMENTPOSTLOGIN: Login
COMMENTS: Kommentare
COMMENTSDISABLED: 'Kommentare sind deaktiviert'
DELETEALLCOMMENTS: 'Alle Kommentare auf dieser Seite löschen'
LOGINTOPOSTCOMMENT: 'Login um zu kommentieren'
NEXT: weiter
NOCOMMENTSYET: 'Noch keine Kommentare auf dieser Seite.'
POSTCOM: Kommentieren
PREV: zurück
RSSFEEDALLCOMMENTS: 'RSS Feed für alle Kommentare'
RSSFEEDCOMMENTS: 'RSS Feed für Kommentare auf dieser Seite'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Zugang zum Bereich ''Kommentare'''
ApprovedComments: 'Gehnehmigt ({count})'
Comments: Genehmigt
MENUTITLE: Kommentare
NewComments: Neu
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Genehmigen
COMMENTAPPROVED: 'Kommentar genehmigt.'
COMMENTMARKEDSPAM: 'Commentar als Spam markiert.'
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Genehmigen
COMMENTAPPROVED: 'Kommentar genehmigt.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Genehmigen
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Commentar als Spam markiert.'
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentare
EMAILADDRESS: 'E-Mail Adresse (wird nicht angezeigt)'
PERMISSIONFAILURE: 'Sie sind nicht berechtigt diese Seite zu kommentieren. Bitte stellen Sie sicher das Sie eingeloggt sind und die nötige Berechtigung besitzen.'
RSSTITLE: 'Kommentare RSS Feed'
WEBSITEURL: 'Website URL'
CommentsAdmin:
NeedsModeration: 'Benötigt Moderation'
CommentsInterface_ss:
COMMENTLOGINERROR: 'Sie können diese Seite nur kommentieren, wenn Sie eingeloggt sind'
COMMENTPERMISSIONERROR: 'und die nötige Berechtigung besitzen'
COMMENTPOSTLOGIN: 'Login'
COMMENTS: Kommentare
COMMENTSDISABLED: 'Kommentare sind deaktiviert'
LOGINTOPOSTCOMMENT: 'Login um zu kommentieren'
NEXT: weiter
NOCOMMENTSYET: 'Noch keine Kommentare auf diese Seite.'
POSTCOM: 'Kommentieren'
PREV: zurück
RSSFEEDALLCOMMENTS: 'RSS Feed für alle Kommentare'
RSSFEEDCOMMENTS: 'RSS Feed für Kommentare auf dieser Seite'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Ihr Kommentar wurde gespeichert und wird durch einen Moderator freigeschaltet.'
CommentsInterface_singlecomment_ss:
APPROVE: 'Diesen Kommetar akzeptieren'
ISNTSPAM: 'Dieser Kommentar ist kein Spam'
ISSPAM: 'Dieser Kommentar ist Spam'
PBY: 'Geschrieben von'
REMCOM: 'Diesen Kommentar entfernen'
PageCommentInterface:
DELETEALLCOMMENTS: 'Alle Kommentare auf dieser Seite entfernen'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTMODERATION: Kommentarmoderation
COMMENTOPTIONS: Kommentare
COMMENTSTABSET: Kommentare
MODERATIONREQUIRED_NONE: 'Keine Moderation benötigt'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Nur Nicht-Mitglieder moderieren'
MODERATIONREQUIRED_REQUIRED: 'Alle Kommentare moderieren'
SilverStripe\Comments\Model\Comment:
COMMENT: Kommentar
COMMENTBY: 'Kommentar von ''%s'''
CREATED: 'Datum geschrieben'
EMAIL: E-Mail
ISSPAM: 'Spam?'
MODERATED: 'Moderiert?'
'ON': am
OPTIONS: Optionen
OPTION_DESCRIPTION: 'Unmoderierte und Spam-Kommentare werden nicht angezeigt, bis sie freigeschaltet sind'
PARENTTITLE: Parent
PLURALNAME: Kommentare
PLURALS:
one: 'Ein Kommentar'
other: '{count} Kommentare'
ParentComment_Title: 'Dieser Kommentar ist eine Antwort auf'
SINGULARNAME: Kommentar
URL: URL

17
lang/el.yml Normal file
View File

@ -0,0 +1,17 @@
el:
CommentInterface:
PREVIEW: Προεπισκόπιση
PREVIEWLABEL: Προεπισκόπιση
CommentsInterface_ss:
COMMENTS: Σχόλια
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Σχόλια
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Σχόλια
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Σχόλια
COMMENTSTABSET: Σχόλια
SilverStripe\Comments\Model\Comment:
EMAIL: Email
PLURALNAME: Σχόλια
URL: URL

View File

@ -1,47 +1,36 @@
en:
Comment:
ALLOWCOMMENTS: 'Allow Comments'
COMMENT: Comment
COMMENTBY: 'Comment by %s'
ISSPAM: 'Spam?'
MODERATED: 'Moderated?'
NAME: 'Author Name'
ON: on
PLURALNAME: Comments
SINGULARNAME: Comment
CREATED: 'Date posted'
EMAIL: Email
PARENTTITLE: Parent
URL: URL
CommentAdmin:
Moderated: Moderated
NeedsModeration: 'Needs Moderation'
MENUTITLE: Comments
ADMIN_PERMISSION: "Access to 'Comments' section"
CommentInterface:
POST: Post
YOURNAME: 'Your name'
COMMENT_MESSAGE_REQUIRED: 'Please enter your comment'
COMMENT_MESSAGE_URL: 'Please enter a valid URL'
EMAILADDRESS_MESSAGE_EMAIL: 'Please enter a valid email address'
EMAILADDRESS_MESSAGE_REQUIRED: 'Please enter your email address'
POST: Post
PREVIEW: Preview
PREVIEWLABEL: Preview
YOURNAME: 'Your name'
YOURNAME_MESSAGE_REQUIRED: 'Please enter your name'
CommentingController:
COMMENTS: Comments
EMAILADDRESS: 'Your email address (will not be published)'
PERMISSIONFAILURE: 'You''re not able to post comments to this page. Please ensure you are logged in and have an appropriate permission level.'
RSSTITLE: 'Comments RSS Feed'
WEBSITEURL: 'Your website URL'
Comments:
COMMENTSREQUIRELOGIN: 'Require login to comment'
CommentsAdmin:
NeedsModeration: 'Needs Moderation'
Comments: Approved
NewComments: New
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Your comment has been submitted and is now awaiting moderation.'
CommentsInterface_singlecomment_ss:
APPROVE: 'Approve it'
ISNTSPAM: 'Not spam'
ISSPAM: 'Spam it'
REMCOM: 'Reject it'
REPLYTO: 'Reply to'
CommentsInterface_ss:
AWAITINGMODERATION: 'Your comment has been submitted and is now awaiting moderation.'
COMMENTLOGINERROR: 'You cannot post comments until you have logged in'
COMMENTPERMISSIONERROR: 'and that you have an appropriate permission level'
COMMENTPERMISSIONERROR: 'and have an appropriate permission level'
COMMENTPOSTLOGIN: 'Login Here'
COMMENTS: Comments
COMMENTSDISABLED: 'Posting comments has been disabled'
DELETEALLCOMMENTS: 'Delete all comments on this page'
LOGINTOPOSTCOMMENT: 'Login to post a comment'
NEXT: next
NOCOMMENTSYET: 'No one has commented on this page yet.'
@ -49,16 +38,59 @@ en:
PREV: previous
RSSFEEDALLCOMMENTS: 'RSS feed for all comments'
RSSFEEDCOMMENTS: 'RSS feed for comments on this page'
AWAITINGMODERATION: 'Your comment has been submitted and is now awaiting moderation.'
PageCommentInterface:
DELETEALLCOMMENTS: 'Delete all comments on this page'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Your comment has been submitted and is now awaiting moderation.'
CommentsInterface_singlecomment_ss:
APPROVE: 'approve this comment'
ISNTSPAM: 'this comment is not spam'
ISSPAM: 'this comment is spam'
PBY: 'Posted by'
REMCOM: 'remove this comment'
PageCommentInterface:
DELETEALLCOMMENTS: 'Delete all comments on this page'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Access to ''Comments'' section'
ApprovedComments: 'Approved ({count})'
Comments: Approved
MENUTITLE: Comments
NewComments: New
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Approve
COMMENTAPPROVED: 'Comment approved.'
COMMENTMARKEDSPAM: 'Comment marked as spam.'
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Approve
COMMENTAPPROVED: 'Comment approved.'
SilverStripe\Comments\Admin\CommentsGridFieldBulkAction\CommentHandler:
CHANGES_APPLIED: 'Changes applied'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Approve
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Comment marked as spam.'
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comments
EMAILADDRESS: 'Your email address (will not be published)'
PERMISSIONFAILURE: 'You''re not able to post comments to this page. Please ensure you are logged in and have an appropriate permission level.'
RSSTITLE: 'Comments RSS Feed'
WEBSITEURL: 'Your website URL'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTMODERATION: 'Comment Moderation'
COMMENTOPTIONS: Comments
COMMENTSTABSET: Comments
MODERATIONREQUIRED_NONE: 'No moderation required'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Only moderate non-members'
MODERATIONREQUIRED_REQUIRED: 'Moderate all comments'
SilverStripe\Comments\Model\Comment:
ALLOWCOMMENTS: 'Allow comments'
COMMENT: Comment
COMMENTBY: 'Comment by %s'
CREATED: 'Date posted'
EMAIL: Email
ISSPAM: 'Spam?'
MODERATED: 'Moderated?'
NAME: 'Author name'
'ON': 'on'
OPTIONS: Options
OPTION_DESCRIPTION: 'Unmoderated and spam comments will not be displayed until approved'
PARENTTITLE: Parent
PLURALNAME: Comments
PLURALS:
one: 'A Comment'
other: '{count} Comments'
ParentComment_Title: 'This comment is a reply to the below'
SINGULARNAME: Comment
URL: URL

15
lang/en_GB.yml Normal file
View File

@ -0,0 +1,15 @@
en_GB:
CommentsInterface_ss:
COMMENTS: Comments
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Comments
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comments
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comments
COMMENTSTABSET: Comments
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': 'on'
OPTIONS: Options
PLURALNAME: Comments

96
lang/eo.yml Normal file
View File

@ -0,0 +1,96 @@
eo:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Bonvole enigu vian komenton'
COMMENT_MESSAGE_URL: 'Bonvole enigu validan URL-on'
EMAILADDRESS_MESSAGE_EMAIL: 'Bonvolu enigi validan retadreson'
EMAILADDRESS_MESSAGE_REQUIRED: 'Bonvolu enigi vian retadreson'
POST: Afiŝo
PREVIEW: Antaŭvido
PREVIEWLABEL: Antaŭvido
YOURNAME: 'Via nomo'
YOURNAME_MESSAGE_REQUIRED: 'Bonvole enigu vian nomon'
Comments:
COMMENTSREQUIRELOGIN: 'Necesas ensaluti por komenti'
CommentsAdmin:
Comments: Aprobita
NewComments: Nova
SpamComments: Spamo
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Via komento afiŝiĝis kaj nun atendas kontrolon.'
CommentsInterface_singlecomment_ss:
APPROVE: 'Aprobi ĝin'
ISNTSPAM: 'Ne spamo'
ISSPAM: 'Trakti kiel spamon'
REMCOM: 'Malakcepti ĝin'
REPLYTO: 'Respondi al'
CommentsInterface_ss:
AWAITINGMODERATION: 'Via komento afiŝiĝis kaj nun atendas kontrolon.'
COMMENTLOGINERROR: 'Vi ne povas afiŝi komenton ĝis vi ensalutis'
COMMENTPERMISSIONERROR: 'kaj le vi havas taŭgan permeson'
COMMENTPOSTLOGIN: 'Ensaluti ĉi tie'
COMMENTS: Komentoj
COMMENTSDISABLED: 'Afiŝi komentojn estas malŝaltita'
DELETEALLCOMMENTS: 'Forigi ĉiujn komentojn en ĉi tiu paĝo'
LOGINTOPOSTCOMMENT: 'Ensaluti por afiŝi komenton'
NEXT: sekva
NOCOMMENTSYET: 'Neniu komentis pri ĉi tiu paĝo ĝis nun.'
POSTCOM: 'Afiŝi vian komenton'
PREV: antaŭa
RSSFEEDALLCOMMENTS: 'RSS-fluo por ĉiuj komentoj'
RSSFEEDCOMMENTS: 'RSS-fluo por komentoj pri ĉi tiu paĝo'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Aliro al sekcio ''Komentoj'''
ApprovedComments: Aprobita
Comments: Aprobita
MENUTITLE: Komentoj
NewComments: Nova
SpamComments: Spamo
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Aprobi
COMMENTAPPROVED: 'Komento aprobita.'
COMMENTMARKEDSPAM: 'Markis komenton kiel spamon'
SPAM: Spamo
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Aprobi
COMMENTAPPROVED: 'Komento aprobita.'
SilverStripe\Comments\Admin\CommentsGridFieldBulkAction\CommentHandler:
CHANGES_APPLIED: 'Aplikis la ŝanĝojn'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Aprobi
SPAM: Spamo
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Markis komenton kiel spamon'
SPAM: Spamo
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentoj
EMAILADDRESS: 'Via retadreso (ne publikiĝos)'
PERMISSIONFAILURE: 'Vi ne povas afiŝi komentojn al ĉi tiu paĝo. Bonvole certigu ke vi jam ensalutis kaj havas taŭgan permeson.'
RSSTITLE: 'RSS-fluo de komentoj'
WEBSITEURL: 'Via reteja URL'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTMODERATION: 'Kontroli komentojn'
COMMENTOPTIONS: Komentoj
COMMENTSTABSET: Komentoj
MODERATIONREQUIRED_NONE: 'Kontrolado ne bezonatas'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Kontroli nur nemembrojn'
MODERATIONREQUIRED_REQUIRED: 'Kontroli ĉiujn komentojn'
SilverStripe\Comments\Model\Comment:
ALLOWCOMMENTS: 'Ebligi komentojn'
COMMENT: Komento
COMMENTBY: 'Komento de %s'
CREATED: 'Dato de afiŝo'
EMAIL: Retpoŝto
ISSPAM: 'Ĉu spamo?'
MODERATED: 'Ĉu kontrolata?'
NAME: 'Nomo de aŭtoro'
'ON': en
OPTIONS: Agordoj
OPTION_DESCRIPTION: 'Ne vidigos nekontrolitajn aŭ spamajn komentojn antaŭ aprobo'
PARENTTITLE: Patra
PLURALNAME: Komentoj
PLURALS:
one: 'Unu komento'
other: '{count} komentoj'
ParentComment_Title: 'Tiu komento estas respondo al la suba'
SINGULARNAME: Komento
URL: URL

49
lang/es.yml Normal file
View File

@ -0,0 +1,49 @@
es:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Por favor, ingres su comentario'
COMMENT_MESSAGE_URL: 'Por favor, ingrese una URL válida'
EMAILADDRESS_MESSAGE_EMAIL: 'Por favor, ingrse una dirección de correo válida'
EMAILADDRESS_MESSAGE_REQUIRED: 'Por favor, ingrese su dirección de correo'
POST: Publicar
PREVIEW: 'Vista previa'
PREVIEWLABEL: 'Vista previa'
YOURNAME: 'Su nombre'
YOURNAME_MESSAGE_REQUIRED: 'Por favor, ingrese su nombre'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Se envió su comentario y ahora está esperando la moderación.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Se envió su comentario y ahora está esperando la moderación.'
COMMENTLOGINERROR: 'Ud no puede comentar hasta que haya iniciado sesión'
COMMENTPERMISSIONERROR: 'y que tenga un nivel de permisos adecuado'
COMMENTPOSTLOGIN: 'Iniciar sesión aquí'
COMMENTS: Comentarios
COMMENTSDISABLED: 'Se deshabilitó la publicación de comentarios'
DELETEALLCOMMENTS: 'Eliminar todos los comentarios de esta página'
LOGINTOPOSTCOMMENT: 'Iniciar sesión para publicar un comentario'
NEXT: siguiente
NOCOMMENTSYET: 'Nadie comentó en esta página todavía.'
POSTCOM: 'Publicar su mensaje'
PREV: anterior
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Acceder a la sección ''Comentarios'''
MENUTITLE: Comentarios
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comentarios
EMAILADDRESS: 'Su dirección de correo (no se publicará)'
PERMISSIONFAILURE: 'Ud no puede publicar comentarios en esta página. Por favor asegúrese que haya iniciado sesión y que tenga el adecuado nivel de permisos.'
WEBSITEURL: 'URL de su sitio Web'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentarios
COMMENTSTABSET: Comentarios
SilverStripe\Comments\Model\Comment:
COMMENT: Comentario
COMMENTBY: 'Comentario por %s'
CREATED: 'Fecha de publicación'
EMAIL: Email
ISSPAM: '¿Spam?'
MODERATED: '¿Moderado?'
'ON': en
PARENTTITLE: Padre
PLURALNAME: Comentarios
SINGULARNAME: Comentario
URL: URL

19
lang/es_MX.yml Normal file
View File

@ -0,0 +1,19 @@
es_MX:
CommentInterface:
PREVIEW: Previo
PREVIEWLABEL: Previo
CommentsInterface_ss:
COMMENTS: Comentarios
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Comentarios
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comentarios
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentarios
COMMENTSTABSET: Comentarios
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': en
OPTIONS: Options
PLURALNAME: Comentarios
URL: URL

19
lang/et_EE.yml Normal file
View File

@ -0,0 +1,19 @@
et_EE:
CommentInterface:
PREVIEW: Eelvaade
PREVIEWLABEL: Eelvaade
CommentsInterface_ss:
COMMENTS: Komentaarid
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentaarid
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentaarid
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentaarid
COMMENTSTABSET: Komentaarid
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': '-'
OPTIONS: Options
PLURALNAME: Komentaarid
URL: URL

84
lang/fa_IR.yml Normal file
View File

@ -0,0 +1,84 @@
fa_IR:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'لطفاً دیدگاه خود را وارد نمایید'
COMMENT_MESSAGE_URL: 'لطفاً نشانی اینترنتی معتبر وارد نمایید'
EMAILADDRESS_MESSAGE_EMAIL: 'لطفاً نشانی ایمیل معتبر وارد نمایید'
EMAILADDRESS_MESSAGE_REQUIRED: 'لطفاً نشانی ایمیل خود را وارد نمایید'
POST: ارسال
PREVIEW: پیش‌نمایش
PREVIEWLABEL: پیش‌نمایش
YOURNAME: 'نام شما'
YOURNAME_MESSAGE_REQUIRED: 'لطفاً نام خود را وارد نمایید'
Comments:
COMMENTSREQUIRELOGIN: 'درج دیدگاه نیازمند ورود شما به سایت است'
CommentsAdmin:
Comments: 'تایید شده'
NewComments: تازه
SpamComments: هرزنوشت
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'دیدگاه شما ارسال شده و هم‌اکنون در انتظار بررسی است.'
CommentsInterface_singlecomment_ss:
REPLYTO: 'ارسال پاسخ به'
CommentsInterface_ss:
AWAITINGMODERATION: 'دیدگاه شما ارسال شده و هم‌اکنون در انتظار بررسی است.'
COMMENTLOGINERROR: 'تا زمانی‌که وارد سایت نشده‌اید نمی‌توانید دیدگاهی درج نمایید'
COMMENTPERMISSIONERROR: 'و اینکه شما دارای سطح دسترسی مناسب هستید'
COMMENTPOSTLOGIN: 'از اینجا وارد سایت شوید'
COMMENTS: دیدگاه‌ها
COMMENTSDISABLED: 'ارسال دیدگاه مسدود است'
DELETEALLCOMMENTS: 'حذف تمامی دیدگاه‌ها در این صفحه'
LOGINTOPOSTCOMMENT: 'برای ارسال دیدگاه وارد شوید'
NEXT: بعدی
NOCOMMENTSYET: 'هنوز هیچکس در این صفحه دیدگاهی ننگاشته است.'
POSTCOM: 'ارسال دیدگاه'
PREV: پیشین
RSSFEEDALLCOMMENTS: 'خوراک آر‌اس‌اس تمامی دیدگاه‌ها'
RSSFEEDCOMMENTS: 'خوراک آر‌اس‌اس دیدگاه‌های این صفحه'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'دسترسی به بخش ''دیدگاه‌ها'''
ApprovedComments: 'تایید شده'
Comments: 'تایید شده'
MENUTITLE: دیدگاه‌ها
NewComments: تازه
SpamComments: هرزنوشت
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: تایید
COMMENTAPPROVED: 'دیدگاه تایید شد.'
COMMENTMARKEDSPAM: 'دیدگاه به عنوان هرزنوشت علامت‌گذاری شد.'
SPAM: هرزنوشت
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: تایید
COMMENTAPPROVED: 'دیدگاه تایید شد.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: تایید
SPAM: هرزنوشت
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'دیدگاه به عنوان هرزنوشت علامت‌گذاری شد.'
SPAM: هرزنوشت
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: دیدگاه‌ها
EMAILADDRESS: 'ایمیل شما (منتشر نخواهد شد)'
PERMISSIONFAILURE: 'شما نمی‌توانید در این صفحه دیدگاهی درج نمایید. لطفاً اطمینان حاصل نمایید که وارد سایت شده‌اید و از سطح دسترسی دسترسی مناسب برخوردار هستید.'
RSSTITLE: 'خوراک آراس‌اس دیدگاه‌ها'
WEBSITEURL: 'نشانی وب‌سایت شما'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: دیدگاه‌ها
COMMENTSTABSET: دیدگاه‌ها
MODERATIONREQUIRED_NONE: 'نیاز به مدیریت ندارد'
MODERATIONREQUIRED_NONMEMBERSONLY: 'تنها مدیریت کاربران غیر عضو'
MODERATIONREQUIRED_REQUIRED: 'مدیریت تمامی دیدگاه‌ها'
SilverStripe\Comments\Model\Comment:
COMMENT: دیدگاه
COMMENTBY: 'دیدگاه توسط %s'
CREATED: 'تاریخ ارسال'
EMAIL: ایمیل
ISSPAM: هرزنوشت؟
MODERATED: 'مدیریت شده؟'
'ON': در
OPTIONS: گزینه‌ها
OPTION_DESCRIPTION: 'دیدگاه‌های مدیریت نشده و هرزنوشت‌ها تا زمان تایید به نمایش در نخواهند آمد'
PARENTTITLE: والد
PLURALNAME: دیدگاه‌ها
ParentComment_Title: 'این دیدگاه پاسخی است به زیر'
SINGULARNAME: دیدگاه
URL: 'نشانی اینترنتی'

View File

@ -1,49 +1,84 @@
fi:
Comment:
ALLOWCOMMENTS: 'Salli kommentointi'
COMMENT: 'Komentti'
COMMENTBY: '''%s'' kommentoi'
ISSPAM: 'Spämmiä?'
MODERATED: 'Moderoitu?'
NAME: 'Kirjoittajan nimi'
ON: ':'
PLURALNAME: 'Kommentit'
SINGULARNAME: 'Komentti'
CommentAdmin:
Moderated: 'Moderoitu'
NeedsModeration: 'Tarvitsee moderointia'
MENUTITLE: 'Kommentit'
CommentInterface:
POST: 'Lähetä'
YOURNAME: 'Nimesi'
CommentingController:
COMMENTS: 'Kommentit'
COMMENT_MESSAGE_REQUIRED: 'Ole hyvä ja kommentoi'
COMMENT_MESSAGE_URL: 'URL-osoitteen on oltava kelvollinen'
EMAILADDRESS_MESSAGE_EMAIL: 'Sähköpostiosoitteen on oltava kelvollinen'
EMAILADDRESS_MESSAGE_REQUIRED: Sähköpostiosoitteesi
POST: Lähetä
PREVIEW: Esikatselu
PREVIEWLABEL: Esikatselu
YOURNAME: Nimesi
YOURNAME_MESSAGE_REQUIRED: Nimesi
Comments:
COMMENTSREQUIRELOGIN: 'Vaadi kirjautuminen kommentointiin'
CommentsAdmin:
Comments: Hyväksytty
NewComments: Uusi
SpamComments: Spämmiä
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Kommenttisi on lähetetty ja odottaa moderointia.'
CommentsInterface_singlecomment_ss:
REPLYTO: Vastausosoite
CommentsInterface_ss:
AWAITINGMODERATION: 'Kommenttisi on lähetetty ja odottaa moderointia.'
COMMENTLOGINERROR: 'Et voi kommentoida ennen kuin olet kirjautunut sisään'
COMMENTPERMISSIONERROR: 'ja käyttöoikeustasosi on oltava riittävä'
COMMENTPOSTLOGIN: 'Kirjaudu tästä'
COMMENTS: Comments
COMMENTSDISABLED: 'Kommentointi on lukittu'
DELETEALLCOMMENTS: 'Poista kaikki tämän sivun kommentit'
LOGINTOPOSTCOMMENT: 'Kirjaudu kommentoidaksesi'
NEXT: seuraava
NOCOMMENTSYET: 'Kukaan ei ole vielä kommentoinut tätä sivua.'
POSTCOM: 'Lähetä kommenttisi'
PREV: edellinen
RSSFEEDALLCOMMENTS: 'RSS-syöte kaikista kommenteista'
RSSFEEDCOMMENTS: 'RSS-syöte tämän sivun kommenteista'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Pääsy Comments-osioon'
ApprovedComments: Hyväksytty
Comments: Hyväksytty
MENUTITLE: Kommentit
NewComments: Uusi
SpamComments: Spämmiä
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Hyväksy
COMMENTAPPROVED: 'Kommentti hyväksytty'
COMMENTMARKEDSPAM: 'Kommentti merkitty spämmiksi.'
SPAM: Spämmiä
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Hyväksy
COMMENTAPPROVED: 'Kommentti hyväksytty'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Hyväksy
SPAM: Spämmiä
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Kommentti merkitty spämmiksi.'
SPAM: Spämmiä
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentit
EMAILADDRESS: 'Sähköpostiosoitteesi (ei julkaista)'
PERMISSIONFAILURE: 'Et voi kommentoida tätä sivua. Varmista, että olet kirjatuneena ja että käyttöoikeutesi ovat riittävät.'
RSSTITLE: 'Kommenttien RSS-syöte'
WEBSITEURL: 'WWW-sivustosi osoite'
CommentsAdmin:
NeedsModeration: 'Tarvitsee moderointia'
CommentsInterface_ss:
COMMENTLOGINERROR: 'Et voi kommentoida ennen kuin olet kirjautunut sisään'
COMMENTPERMISSIONERROR: 'ja käyttöoikeustasosi on oltava riittävä'
COMMENTPOSTLOGIN: 'Kirjaudu tästä'
COMMENTS: 'Comments'
COMMENTSDISABLED: 'Kommentointi on lukittu'
LOGINTOPOSTCOMMENT: 'Kirjaudu kommentoidaksesi'
NEXT: 'seuraava'
NOCOMMENTSYET: 'Kukaan ei ole vielä kommentoinut tätä sivua.'
POSTCOM: 'Lähetä kommenttisi'
PREV: 'edellinen'
RSSFEEDALLCOMMENTS: 'RSS-syöte kaikista kommenteista'
RSSFEEDCOMMENTS: 'RSS-syöte tämän sivun kommenteista'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Kommenttisi on lähetetty ja on moderoitavana.'
CommentsInterface_singlecomment_ss:
APPROVE: 'hyväksy kommentti'
ISNTSPAM: 'kommentti ei ole spämmiä'
ISSPAM: 'kommentti on spämmiä'
PBY: 'Lähettänyt'
REMCOM: 'poista kommentti'
PageCommentInterface:
DELETEALLCOMMENTS: 'Poista kaikki tämän sivun kommentit'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Kommentit
COMMENTSTABSET: Kommentit
MODERATIONREQUIRED_NONE: 'Moderointia ei tarvita'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Moderoi vain ei-jäsenet'
MODERATIONREQUIRED_REQUIRED: 'Moderoi kaikki kommentit'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentti
COMMENTBY: 'Kommentoija: %s'
CREATED: Kirjoitettu
EMAIL: Sähköposti
ISSPAM: 'Spämmiä?'
MODERATED: 'Moderoitu?'
'ON': pvm
OPTIONS: Asetukset
OPTION_DESCRIPTION: 'Moderoimattomat ja roskakommentit eivät näy ennen hyväksymistä'
PARENTTITLE: Isäntä
PLURALNAME: Kommentit
ParentComment_Title: 'Tämä kommentti on vastaus alla olevaan'
SINGULARNAME: Kommentti
URL: URL-osoite

15
lang/fi_FI.yml Normal file
View File

@ -0,0 +1,15 @@
fi_FI:
CommentsInterface_ss:
COMMENTS: Kommentit
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Kommentit
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentit
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Kommentit
COMMENTSTABSET: Kommentit
SilverStripe\Comments\Model\Comment:
EMAIL: Sähköposti
OPTIONS: Valinnat
PLURALNAME: Kommentit
URL: URL-osoite

51
lang/fr.yml Normal file
View File

@ -0,0 +1,51 @@
fr:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Saisissez votre commentaire'
COMMENT_MESSAGE_URL: 'Saisissez une URl valide'
EMAILADDRESS_MESSAGE_EMAIL: 'Saisissez une adresse mail valide'
EMAILADDRESS_MESSAGE_REQUIRED: 'Saisissez votre adresse e-mail'
POST: Publier
PREVIEW: Aperçu
PREVIEWLABEL: Aperçu
YOURNAME: 'Votre Nom'
YOURNAME_MESSAGE_REQUIRED: 'Saisissez votre nom'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Votre commentaire a été soumis à modération.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Votre commentaire a été soumis à modération.'
COMMENTLOGINERROR: 'Vous ne pouvez pas poster de commentaires sans être connecté'
COMMENTPERMISSIONERROR: 'et d''avoir le bon niveau de d''autorisation'
COMMENTPOSTLOGIN: 'Se connecter'
COMMENTS: Commentaires
COMMENTSDISABLED: 'La publication de commentaires est désactivée'
DELETEALLCOMMENTS: 'Supprimer tout les commentaires de cette page'
LOGINTOPOSTCOMMENT: 'Connectez vous pour poster un commentaire'
NEXT: suivant
NOCOMMENTSYET: 'Il n''y a pas encore de commentaire.'
POSTCOM: 'Publiez votre commentaire'
PREV: précédent
RSSFEEDALLCOMMENTS: 'Flux RSS de tous les commentaires'
RSSFEEDCOMMENTS: 'Flux RSS de commentaires de cette page'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Commentaires
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Commentaires
EMAILADDRESS: 'Votre adresse e-mail (ne sera pas publiée)'
PERMISSIONFAILURE: 'Vous n''avez pas l''autorisation de commenter cette page. Assurez vous d''être connecté et d''avoir le bon niveau de d''autorisation.'
RSSTITLE: 'Flux RSS de Commentaires'
WEBSITEURL: 'URl de votre site web'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Commentaires
COMMENTSTABSET: Commentaires
SilverStripe\Comments\Model\Comment:
COMMENT: Commentaire
COMMENTBY: 'Commentaire par %s'
CREATED: 'Date de publication'
EMAIL: Email
ISSPAM: 'Spam?'
MODERATED: 'Modéré?'
'ON': sur
PARENTTITLE: Parent
PLURALNAME: Commentaires
SINGULARNAME: Commentaire
URL: URL

16
lang/gl_ES.yml Normal file
View File

@ -0,0 +1,16 @@
gl_ES:
CommentInterface:
PREVIEW: Previsualizar
PREVIEWLABEL: Previsualizar
CommentsInterface_ss:
COMMENTS: Comentarios
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Comentarios
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comentarios
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentarios
COMMENTSTABSET: Comentarios
SilverStripe\Comments\Model\Comment:
EMAIL: Correo-e
PLURALNAME: Comentarios

17
lang/he_IL.yml Normal file
View File

@ -0,0 +1,17 @@
he_IL:
CommentInterface:
PREVIEW: 'תצוגה מקדימה'
PREVIEWLABEL: 'תצוגה מקדימה'
CommentsInterface_ss:
COMMENTS: תגובות
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: תגובות
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: תגובות
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: תגובות
COMMENTSTABSET: תגובות
SilverStripe\Comments\Model\Comment:
EMAIL: 'דואר אלקטרוני'
PLURALNAME: תגובות
URL: 'כתובת הדף'

84
lang/hr.yml Normal file
View File

@ -0,0 +1,84 @@
hr:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Molimo unesite vaš komentar'
COMMENT_MESSAGE_URL: 'Molimo unesite ispravan link'
EMAILADDRESS_MESSAGE_EMAIL: 'Molimo unesite ispravnu email adresu'
EMAILADDRESS_MESSAGE_REQUIRED: 'Molimo unesite email adresu'
POST: Objava
PREVIEW: Pregled
PREVIEWLABEL: Pregled
YOURNAME: 'Vaše ime'
YOURNAME_MESSAGE_REQUIRED: 'Molimo unesite vaše ime'
Comments:
COMMENTSREQUIRELOGIN: 'Zahtjeva prijavu za komentiranje'
CommentsAdmin:
Comments: Odobren
NewComments: Novo
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Vaš komentar je zaprimljen i čeka odobrenje.'
CommentsInterface_singlecomment_ss:
REPLYTO: 'Odgovori na'
CommentsInterface_ss:
AWAITINGMODERATION: 'Vaš komentar je zaprimljen i čeka odobrenje.'
COMMENTLOGINERROR: 'Ne možete objavljivati komentare dok se ne prijavite'
COMMENTPERMISSIONERROR: 'i da imate potrebna prava'
COMMENTPOSTLOGIN: 'Prijavi se ovdje'
COMMENTS: Komentari
COMMENTSDISABLED: 'Objavljivanje komentara je onemogućeno'
DELETEALLCOMMENTS: 'Obriši sve komentare na ovoj stranici'
LOGINTOPOSTCOMMENT: 'Prijavi se da objaviš komentar'
NEXT: sljedeći
NOCOMMENTSYET: 'Nitko nije komentirao na ovu stranicu.'
POSTCOM: 'Objavi svoj komentar'
PREV: prethodni
RSSFEEDALLCOMMENTS: 'RSS kanal za sve komentare'
RSSFEEDCOMMENTS: 'RSS kanal za sve komentare na ovoj stranici'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Pristup ''Comments'' sekciji'
ApprovedComments: Odobren
Comments: Odobren
MENUTITLE: Komentari
NewComments: Novo
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Odobri
COMMENTAPPROVED: 'Komentar odobren.'
COMMENTMARKEDSPAM: 'Komentar označen kao spam.'
SPAM: SPam
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Odobri
COMMENTAPPROVED: 'Komentar odobren.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Odobri
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Komentar označen kao spam.'
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentari
EMAILADDRESS: 'Vaša email adresa (neće biti objavljena)'
PERMISSIONFAILURE: 'Ne možete objavljivati komentare na ovoj stranici. Molimo provjerite da li ste prijavljeni i imate potrebna prava.'
RSSTITLE: 'RSS kanal komentara'
WEBSITEURL: 'Vaša web adresa'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentari
COMMENTSTABSET: Komentari
MODERATIONREQUIRED_NONE: 'Nije potrebna moderacija'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Samo moderiraj goste'
MODERATIONREQUIRED_REQUIRED: 'Moderiraj sve komentare'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentar
COMMENTBY: 'Komentar od %s'
CREATED: 'Datum objave'
EMAIL: Email
ISSPAM: 'Spam?'
MODERATED: 'Moderirano?'
'ON': na
OPTIONS: Opcije
OPTION_DESCRIPTION: 'Nemoderirani i spam komentari neće biti objavljeni dok se ne odobre'
PARENTTITLE: Roditelj
PLURALNAME: Komentari
ParentComment_Title: 'Ovaj komentar je odgovor na dolje'
SINGULARNAME: Komentar
URL: Link

51
lang/hu.yml Normal file
View File

@ -0,0 +1,51 @@
hu:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Szöveg megadása'
COMMENT_MESSAGE_URL: 'Kérjük adjon meg egy működő linket'
EMAILADDRESS_MESSAGE_EMAIL: 'Kérjük adjon meg egy e-mail címet'
EMAILADDRESS_MESSAGE_REQUIRED: 'Kérjük adja meg az e-mail címét'
POST: Elküld
PREVIEW: Előnézet
PREVIEWLABEL: Előnézet
YOURNAME: 'Az Ön neve'
YOURNAME_MESSAGE_REQUIRED: 'Kérjük adja meg nevét'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Hozzászólását moderálásra továbbítottuk'
CommentsInterface_ss:
AWAITINGMODERATION: 'Hozzászólását moderálásra továbbítottuk'
COMMENTLOGINERROR: 'A hozzászóláshoz be kell jelentkeznie'
COMMENTPERMISSIONERROR: 'és rendelkeznie kell a megfelelő jogosultsággal'
COMMENTPOSTLOGIN: Bejelentkezés
COMMENTS: Hozzászólások
COMMENTSDISABLED: 'A hozzászólások le vannak tiltva '
DELETEALLCOMMENTS: 'Oldal összes hozzászólásának törlése'
LOGINTOPOSTCOMMENT: 'A hozzászóláshoz jelentkezzen be '
NEXT: következő
NOCOMMENTSYET: 'Nem érkeztek hozzászólások'
POSTCOM: 'Küldjön egy hozzászólást'
PREV: előző
RSSFEEDALLCOMMENTS: 'Összes hozzászólás RSS-ben'
RSSFEEDCOMMENTS: 'Oldal hozzászólásai RSS-ben'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: '''Hozzászólások'' mező elérése'
MENUTITLE: Hozzászólások
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: 'A hosszászólás szövege'
EMAILADDRESS: 'Az Ön e-mail címe (nem látható)'
PERMISSIONFAILURE: 'Ön csak a bejelentkezést követően, és a megfelelő jogosultság birtokában hozhat létre hozzászólást.'
RSSTITLE: 'Hozzászólások RSS-ben'
WEBSITEURL: 'Az Ön webcíme'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Hozzászólások
COMMENTSTABSET: Hozzászólások
SilverStripe\Comments\Model\Comment:
COMMENT: Hozzászólás
COMMENTBY: 'Létrehozta %s'
CREATED: 'Létrehozás dátuma'
EMAIL: E-mail
ISSPAM: 'SPAM?'
MODERATED: 'Moderált?'
PARENTTITLE: Szülő
PLURALNAME: Hozzászólások
SINGULARNAME: Hozzászólás
URL: Link

50
lang/id.yml Normal file
View File

@ -0,0 +1,50 @@
id:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Ketikkan komentar Anda'
COMMENT_MESSAGE_URL: 'Isikan URL yang valid'
EMAILADDRESS_MESSAGE_EMAIL: 'Isikan alamat email yang valid'
EMAILADDRESS_MESSAGE_REQUIRED: 'Isikan alamat email Anda'
POST: Kirim
PREVIEW: Pratinjau
PREVIEWLABEL: Pratinjau
YOURNAME: 'Nama Anda'
YOURNAME_MESSAGE_REQUIRED: 'Isikan nama Anda'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Komentar Anda sudah terkirim dan menunggu moderasi.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Komentar Anda sudah terkirim dan menunggu moderasi.'
COMMENTLOGINERROR: 'Anda tidak dapat mengomentari jika belum login'
COMMENTPERMISSIONERROR: 'dan memiliki hak akses yang sesuai'
COMMENTPOSTLOGIN: 'Login di Sini'
COMMENTS: Komentar
COMMENTSDISABLED: 'Komentar sedang dinonaktifkan'
DELETEALLCOMMENTS: 'Hapus semua komentar di laman ini'
LOGINTOPOSTCOMMENT: 'Login untuk berkomentar'
NEXT: selanjutnya
NOCOMMENTSYET: 'Belum ada yang berkomentar.'
POSTCOM: 'Kirim komentar Anda'
PREV: sebelumnya
RSSFEEDALLCOMMENTS: 'Sindikasi RSS untuk semua komentar'
RSSFEEDCOMMENTS: 'Sindikasi RSS untuk komentar di laman ini'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentar
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentar
EMAILADDRESS: 'Alamat email Anda (tidak akan diterbitkan)'
PERMISSIONFAILURE: 'Anda tidak dapat mengomentari laman ini. Mohon pastikan Anda sudah login dan mendapatkan hak akses yang sesuai.'
RSSTITLE: 'Sindikasi RSS Komentar'
WEBSITEURL: 'URL situs Anda'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentar
COMMENTSTABSET: Komentar
SilverStripe\Comments\Model\Comment:
COMMENT: Komentar
COMMENTBY: 'Komentar oleh %s'
CREATED: 'Tanggal kirim'
EMAIL: Email
ISSPAM: 'Spam?'
MODERATED: 'Moderasi?'
PARENTTITLE: Induk
PLURALNAME: Komentar
SINGULARNAME: Komentar
URL: URL

15
lang/is.yml Normal file
View File

@ -0,0 +1,15 @@
is:
CommentsInterface_ss:
COMMENTS: Athugasemdir
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Athugasemdir
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Athugasemdir
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Athugasemdir
COMMENTSTABSET: Athugasemdir
SilverStripe\Comments\Model\Comment:
EMAIL: Tölvupóstur
'ON': á
PLURALNAME: Athugasemdir
URL: 'Veffang (URL)'

22
lang/it.yml Normal file
View File

@ -0,0 +1,22 @@
it:
CommentInterface:
PREVIEW: Anteprima
PREVIEWLABEL: Anteprima
CommentsInterface_ss:
COMMENTS: Commenti
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Commenti
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Commenti
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Commenti
COMMENTSTABSET: Commenti
SilverStripe\Comments\Model\Comment:
COMMENT: Commento
EMAIL: E-mail
'ON': il
OPTIONS: Opzioni
PARENTTITLE: Parente
PLURALNAME: Commenti
SINGULARNAME: Commento
URL: URL

18
lang/ja.yml Normal file
View File

@ -0,0 +1,18 @@
ja:
CommentInterface:
PREVIEW: プレビュー
PREVIEWLABEL: プレビュー
CommentsInterface_ss:
COMMENTS: コメント
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: コメント
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: コメント
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: コメント
COMMENTSTABSET: コメント
SilverStripe\Comments\Model\Comment:
EMAIL: メール
OPTIONS: オプション
PLURALNAME: コメント
URL: URL

16
lang/ja_JP.yml Normal file
View File

@ -0,0 +1,16 @@
ja_JP:
CommentInterface:
PREVIEW: プレビュー
PREVIEWLABEL: プレビュー
CommentsInterface_ss:
COMMENTS: コメント
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: コメント
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: コメント
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: コメント
COMMENTSTABSET: コメント
SilverStripe\Comments\Model\Comment:
EMAIL: メールアドレス
PLURALNAME: コメント

17
lang/ko.yml Normal file
View File

@ -0,0 +1,17 @@
ko:
CommentInterface:
PREVIEW: 미리보기
PREVIEWLABEL: 미리보기
CommentsInterface_ss:
COMMENTS: 코멘트
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: 코멘트
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: 코멘트
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: 코멘트
COMMENTSTABSET: 코멘트
SilverStripe\Comments\Model\Comment:
EMAIL: 이메일
PLURALNAME: 코멘트
URL: URL

View File

@ -1,49 +1,73 @@
lt:
Comment:
ALLOWCOMMENTS: 'Įgalinti komentarus'
COMMENT: Komentaras
COMMENTBY: 'Komentarą parašė ''%s'''
ISSPAM: 'Šlamštas?'
MODERATED: 'Moderated?'
NAME: 'Autoriaus Vardas'
ON: puslapyje
PLURALNAME: Komentarai
SINGULARNAME: Komentaras
CommentAdmin:
Moderated: Patvirtinti
NeedsModeration: 'Laukia patvirtinimo'
MENUTITLE: Komentarai
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Prašome parašyti savo komentarą'
COMMENT_MESSAGE_URL: 'Prašome įvesti teisingą URL adresą'
EMAILADDRESS_MESSAGE_EMAIL: 'Prašome įvesti teisingą el. pašto adresą'
EMAILADDRESS_MESSAGE_REQUIRED: 'Prašome įvesti el. pašto adresą'
POST: Skelbti
PREVIEW: Peržiūra
PREVIEWLABEL: Peržiūra
YOURNAME: 'Jūsų vardas'
CommentingController:
YOURNAME_MESSAGE_REQUIRED: 'Prašome įvesti savo vardą'
Comments:
COMMENTSREQUIRELOGIN: 'Būtina prisijungti komentavimui'
CommentsAdmin:
Comments: Patvirtinti
NewComments: Nauji
SpamComments: Šlamštas
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Jūsų komentaras išsiųstas ir laukia patvirtinimo.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Jūsų komentaras išsiųstas ir laukia patvirtinimo.'
COMMENTLOGINERROR: 'Negalite rašyti komentarų, jeigu nesate prisijungę'
COMMENTPERMISSIONERROR: 'ir neturėdami atitinkamo teisių lygio'
COMMENTPOSTLOGIN: 'Prisijungti čia'
COMMENTS: Komentarai
COMMENTSDISABLED: 'Komentarų rašymas išjungtas'
DELETEALLCOMMENTS: 'Ištrinti visus komentarus šiame puslapyje'
LOGINTOPOSTCOMMENT: 'Prisijunkite, norėdami parašyti komentarą'
NEXT: sekantys
NOCOMMENTSYET: 'Šio puslapio dar niekas nepakomentavo.'
POSTCOM: 'Parašykite komentarą'
PREV: ankstesni
RSSFEEDALLCOMMENTS: 'Visų komentarų RSS'
RSSFEEDCOMMENTS: 'Šio puslapio komentarų RSS'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Patekti į ''Komentarų'' dalį'
ApprovedComments: Patvirtinti
Comments: Patvirtinti
MENUTITLE: Komentarai
NewComments: Nauji
SpamComments: Šlamštas
SilverStripe\Comments\Admin\CommentsGridFieldAction:
SPAM: Šlamštas
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
SPAM: Šlamštas
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
SPAM: Šlamštas
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentarai
EMAILADDRESS: 'Jūsų el.pašto adresas (nebus skelbiamas)'
PERMISSIONFAILURE: 'Jūs negalite skelbti komentarų šiame puslapyje. Įsitikinkite, kad esate prisijungęs ir turite atitinkamą teisių lygį.'
RSSTITLE: 'Komentarų RSS srautas'
WEBSITEURL: 'Jūsų svetainės nuoroda'
CommentsAdmin:
NeedsModeration: 'Laukia patvirtinimo'
CommentsInterface_ss:
COMMENTLOGINERROR: 'Negalite rašyti komentarų neprisijungęs'
COMMENTPERMISSIONERROR: 'ir neturėdamas atitinkamo teisių lygio'
COMMENTPOSTLOGIN: 'Prisijungti čia'
COMMENTS: Komentarai
COMMENTSDISABLED: 'Komentarų skelbimas išjungtas'
LOGINTOPOSTCOMMENT: 'Prisijunkite, norėdami parašyti komentarą'
NEXT: sekantys
NOCOMMENTSYET: 'Šio puslapio kol kas niekas nepakomentavo.'
POSTCOM: 'Parašykite komentarą'
PREV: ankstesni
RSSFEEDALLCOMMENTS: 'Visų komentarų RSS'
RSSFEEDCOMMENTS: 'Šio puslapio komentarų RSS'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Jūsų komentaras išsiųstas ir laukia patvirtinimo.'
CommentsInterface_singlecomment_ss:
APPROVE: 'patvirtinti šį komentarą'
ISNTSPAM: 'šis komentaras nėra šlamštas'
ISSPAM: 'šis komentaras yra šlamštas'
PBY: Parašė
REMCOM: 'ištrinti šį komentarą'
PageCommentInterface:
DELETEALLCOMMENTS: 'Ištrinti visus komentarus šiame puslapyje'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentarai
COMMENTSTABSET: Komentarai
MODERATIONREQUIRED_NONE: 'Nereikalauja patvirtinimo'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Tvirtinti tik neregistruotų vartotojų'
MODERATIONREQUIRED_REQUIRED: 'Tvirtinti visus komentarus'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentaras
COMMENTBY: 'Komentarą parašė ''%s'''
CREATED: 'Paskelbimo data'
EMAIL: 'E. paštas'
ISSPAM: 'Šlamštas?'
MODERATED: 'Patvirtintas?'
'ON': į
OPTION_DESCRIPTION: 'Nepatvirtinti komentarai nebus rodomi, kol jie atskirai nebus patvirtinti'
PARENTTITLE: Priklauso
PLURALNAME: Komentarai
ParentComment_Title: 'Šis komentaras yra atsakymas į žemiau esantį'
SINGULARNAME: Komentaras
URL: 'URL adresas'

21
lang/mi.yml Normal file
View File

@ -0,0 +1,21 @@
mi:
CommentInterface:
PREVIEW: Arokite
PREVIEWLABEL: Arokite
CommentsInterface_ss:
COMMENTS: 'Ngā Tākupu'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: 'Ngā Tākupu'
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: 'Ngā Tākupu'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: 'Ngā Tākupu'
COMMENTSTABSET: 'Ngā Tākupu'
SilverStripe\Comments\Model\Comment:
COMMENT: Tākupu
EMAIL: Īmēra
OPTIONS: 'Ngā Kōwhiringa'
PARENTTITLE: Matua
PLURALNAME: 'Ngā Tākupu'
SINGULARNAME: Tākupu
URL: PRO

View File

@ -1,39 +1,20 @@
nb:
Comment:
ALLOWCOMMENTS: 'Tillat kommentarer'
COMMENT: Kommentar
COMMENTBY: 'Kommentar av ''%s'''
ISSPAM: 'Spam?'
MODERATED: 'Moderert?'
NAME: 'Navn'
EMAIL: 'Epost'
URL: 'Nettadresse'
CREATED: 'Opprettelsesdato'
PARENTTITLE: 'Overordnet'
ON:
PLURALNAME: Kommentarer
SINGULARNAME: Kommentar
CommentAdmin:
Moderated: Moderert
NeedsModeration: 'Trenger moderering'
MENUTITLE: Kommentarer
CommentInterface:
POST: Legg ut
YOURNAME: 'Navn'
CommentingController:
COMMENTS: Kommentar
EMAILADDRESS: 'Epostadresse (blir ikke publisert)'
PERMISSIONFAILURE: 'Du kan ikke kommentere på denne siden. Vennligst sjekk at du er innlogget og har et passende tilgangsnivå.'
RSSTITLE: 'RSS-feed for kommentarer'
WEBSITEURL: 'Hjemmeside'
CommentsAdmin:
NeedsModeration: 'Trenger moderering'
EMAILADDRESS_MESSAGE_REQUIRED: 'Vennligst fyll inn din e-postadresse'
POST: 'Legg ut'
PREVIEW: Forhåndsvisning
PREVIEWLABEL: Forhåndsvisning
YOURNAME: Navn
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Kommentaren din har blitt lagret og er lagt i kø for moderering.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Kommentaren din har blitt lagret og er lagt i kø for moderering.'
COMMENTLOGINERROR: 'Du kan ikke skrive kommentarer før du har logget inn'
COMMENTPERMISSIONERROR: 'og at du har et passende tilgangsnivå'
COMMENTPOSTLOGIN: 'Logg inn her'
COMMENTS: Kommentarer
COMMENTSDISABLED: 'Kommentarskriving har blitt slått av'
DELETEALLCOMMENTS: 'Fjern alle kommentarer på denne siden'
LOGINTOPOSTCOMMENT: 'Logg inn for å skrive en kommentar'
NEXT: neste
NOCOMMENTSYET: 'Ingen har skrevet en kommentar ennå.'
@ -41,13 +22,22 @@ nb:
PREV: forrige
RSSFEEDALLCOMMENTS: 'RSS-feed for alle kommentarer'
RSSFEEDCOMMENTS: 'RSS-feed for kommentarer på denne siden'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Kommentaren din har blitt lagret og er lagt i kø for moderering.'
CommentsInterface_singlecomment_ss:
APPROVE: 'Godta kommentar'
ISNTSPAM: 'Kommentaren er ikke spam'
ISSPAM: 'Merk som spam'
PBY: 'Skrevet av'
REMCOM: 'Fjern kommentar'
PageCommentInterface:
DELETEALLCOMMENTS: 'Fjern alle kommentarer på denne siden'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Kommentarer
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentar
EMAILADDRESS: 'Epostadresse (blir ikke publisert)'
PERMISSIONFAILURE: 'Du kan ikke kommentere på denne siden. Vennligst sjekk at du er innlogget og har et passende tilgangsnivå.'
RSSTITLE: 'RSS-feed for kommentarer'
WEBSITEURL: Hjemmeside
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Kommentarer
COMMENTSTABSET: Kommentarer
SilverStripe\Comments\Model\Comment:
COMMENT: Kommentar
EMAIL: Epost
ISSPAM: 'Spam?'
MODERATED: 'Moderert?'
PLURALNAME: Kommentarer
SINGULARNAME: Kommentar
URL: Nettadresse

View File

@ -1,35 +1,30 @@
nl:
Comment:
ALLOWCOMMENTS: 'Reacties toestaan'
COMMENT: Reactie
COMMENTBY: 'Reactie door ''%s'''
ISSPAM: 'Spam?'
MODERATED: 'Gemodereerd?'
NAME: 'Auteur'
ON: op
PLURALNAME: Reacties
SINGULARNAME: Reactie
CommentAdmin:
Moderated: Gemodereerd
NeedsModeration: 'Wacht op moderatie'
MENUTITLE: Reacties
CommentInterface:
POST: Plaats reactie
YOURNAME: 'Naam'
CommentingController:
COMMENTS: Reacties
EMAILADDRESS: 'E-mail adres (wordt niet gepubliceerd)'
PERMISSIONFAILURE: 'Je mag geen reacties plaatsen op deze pagina. Log a.u.b. eerst in en controleer je gebruikersrechten.'
RSSTITLE: 'Reacties RSS feed'
WEBSITEURL: 'Website URL'
COMMENT_MESSAGE_REQUIRED: 'Plaats een reactie'
COMMENT_MESSAGE_URL: 'Vul een geldige URL in'
EMAILADDRESS_MESSAGE_EMAIL: 'Gelieve een geldig email adres in te voeren'
EMAILADDRESS_MESSAGE_REQUIRED: 'Gelieve een email adres in te voeren.'
POST: 'Plaats reactie'
PREVIEW: Voorbeeld
PREVIEWLABEL: Voorbeeld
YOURNAME: Naam
YOURNAME_MESSAGE_REQUIRED: 'Gelieve een naam in te voeren.'
Comments:
COMMENTSREQUIRELOGIN: 'Log in om een reactie te plaatsen'
CommentsAdmin:
NeedsModeration: 'Wacht op moderatie'
Comments: Goedgekeurd
NewComments: Nieuw
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Je reactie is verstuurd en is in afwachting van moderatie.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Je reactie is verstuurd en is in afwachting van moderatie.'
COMMENTLOGINERROR: 'Je moet ingelogd zijn om reacties te kunnen plaatsen'
COMMENTPERMISSIONERROR: 'en controleer je gebruikersrechten'
COMMENTPOSTLOGIN: 'Inloggen'
COMMENTPOSTLOGIN: Inloggen
COMMENTS: Reacties
COMMENTSDISABLED: 'Reacties plaatsen is uitgeschakeld'
DELETEALLCOMMENTS: 'Verwijder alle reacties op deze pagina'
LOGINTOPOSTCOMMENT: 'Log in om een reactie te plaatsen'
NEXT: volgende
NOCOMMENTSYET: 'Er zijn nog geen reacties.'
@ -37,13 +32,33 @@ nl:
PREV: vorige
RSSFEEDALLCOMMENTS: 'RSS feed voor alle reacties'
RSSFEEDCOMMENTS: 'RSS feed voor reacties op deze pagina'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Je reactie is verstuurd en is in afwachting van moderatie.'
CommentsInterface_singlecomment_ss:
APPROVE: 'reactie goedkeuren'
ISNTSPAM: 'deze reactie is geen spam'
ISSPAM: 'deze reactie is spam'
PBY: 'Geplaatst door'
REMCOM: 'verwijder deze reactie'
PageCommentInterface:
DELETEALLCOMMENTS: 'Verwijder alle reacties op deze pagina'
SilverStripe\Comments\Admin\CommentAdmin:
Comments: Goedgekeurd
MENUTITLE: Reacties
NewComments: Nieuw
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Reacties
EMAILADDRESS: 'E-mail adres (wordt niet gepubliceerd)'
PERMISSIONFAILURE: 'Je mag geen reacties plaatsen op deze pagina. Log a.u.b. eerst in en controleer je gebruikersrechten.'
RSSTITLE: 'Reacties RSS feed'
WEBSITEURL: 'Website URL'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Reacties
COMMENTSTABSET: Reacties
SilverStripe\Comments\Model\Comment:
COMMENT: Reactie
EMAIL: E-mail
ISSPAM: 'Spam?'
MODERATED: 'Gemodereerd?'
'ON': op
OPTIONS: Instellingen
PLURALNAME: Reacties
SINGULARNAME: Reactie
URL: URL

20
lang/pl.yml Normal file
View File

@ -0,0 +1,20 @@
pl:
CommentInterface:
EMAILADDRESS_MESSAGE_REQUIRED: 'Proszę podać adres email'
POST: Post
PREVIEW: Podgląd
PREVIEWLABEL: Podgląd
CommentsInterface_ss:
COMMENTS: Komentarze
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentarze
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentarze
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentarze
COMMENTSTABSET: Komentarze
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': Opublikowano
PLURALNAME: Komentarze
URL: 'Adres URL'

74
lang/pl_PL.yml Normal file
View File

@ -0,0 +1,74 @@
pl_PL:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Wprowadź swój komentarz'
COMMENT_MESSAGE_URL: 'Wprowadź poprawny adres URL'
EMAILADDRESS_MESSAGE_EMAIL: 'Wprowadź poprawny adres email'
EMAILADDRESS_MESSAGE_REQUIRED: 'Wprowadź swój adres email'
POST: Wyślij
PREVIEW: Podgląd
PREVIEWLABEL: Podgląd
YOURNAME: 'Twoja nazwa'
YOURNAME_MESSAGE_REQUIRED: 'Wprowadź swoje imie'
Comments:
COMMENTSREQUIRELOGIN: 'Zaloguj się aby dodać komentarz'
CommentsAdmin:
Comments: Zatwierdzone
NewComments: Nowe
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Twój komentarz został wysłany i czeka na akceptację przez moderatora.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Twój komentarz został wysłany i czeka na akceptację przez moderatora.'
COMMENTLOGINERROR: 'Nie można dodać komentarza dopóki nie będziesz zalogowany'
COMMENTPERMISSIONERROR: 'i że masz odpowiednie uprawnienia'
COMMENTPOSTLOGIN: Zaloguj
COMMENTS: Komentarze
COMMENTSDISABLED: 'Dodawanie komentarzy zostało wyłączone'
DELETEALLCOMMENTS: 'Usuń wszystkie komentarze na tej stronie'
LOGINTOPOSTCOMMENT: 'Zalogouj się aby dodać komentarz'
NEXT: następny
NOCOMMENTSYET: 'Nikt jeszcze nie skomentował'
POSTCOM: 'Dodaj komentarz'
PREV: poprzedni
RSSFEEDALLCOMMENTS: 'Kanał RSS dla wszystkich komentarzy'
RSSFEEDCOMMENTS: 'Kanał RSS dla komentarzy na tej stronie'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Dostęp do sekcji "Komentarze"'
ApprovedComments: Zatwierdzone
Comments: Zatwierdzone
MENUTITLE: Komentarze
NewComments: Nowy
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentarze
EMAILADDRESS: 'Twój adres email (nie będzie widoczny)'
PERMISSIONFAILURE: 'Nie można dodać komentarza na tej stronie. Upewnij się, że jesteś zalogowany i masz odpowednie uprawnienia.'
RSSTITLE: 'Komentarze z RSS'
WEBSITEURL: 'Adres Twojej strony'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentarze
COMMENTSTABSET: Komentarze
MODERATIONREQUIRED_NONE: 'Komentarze nie wymagają moderacji'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Moderuj tylko niezarejestrowanych użytkowników'
MODERATIONREQUIRED_REQUIRED: 'Moderuj wszystkie komentarze'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentarz
COMMENTBY: 'Komentarz %s'
CREATED: 'Data opublikowania'
EMAIL: Email
ISSPAM: 'Spam?'
MODERATED: 'Moderowane?'
'ON': na
OPTIONS: Opcje
OPTION_DESCRIPTION: 'Niemoderowane i oznaczone jako spam komentarze nie będą wyświetlane do momentu zatwierdzenia'
PARENTTITLE: Rodzic
PLURALNAME: Komentarze
ParentComment_Title: 'Ten komentarz jest odpowiedzią na komentarz poniżej'
SINGULARNAME: Komentarz
URL: URL

16
lang/pt.yml Normal file
View File

@ -0,0 +1,16 @@
pt:
CommentInterface:
PREVIEW: Pré-visualização
PREVIEWLABEL: Pré-visualização
CommentsInterface_ss:
COMMENTS: Comentários
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Comentários
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comentários
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentários
COMMENTSTABSET: Comentários
SilverStripe\Comments\Model\Comment:
'ON': em
PLURALNAME: Comentários

15
lang/pt_PT.yml Normal file
View File

@ -0,0 +1,15 @@
pt_PT:
CommentsInterface_ss:
COMMENTS: Comentários
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Comentários
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Comentários
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentários
COMMENTSTABSET: Comentários
SilverStripe\Comments\Model\Comment:
EMAIL: Email
'ON': em
OPTIONS: Options
PLURALNAME: Comentários

51
lang/ro.yml Normal file
View File

@ -0,0 +1,51 @@
ro:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Vă rugăm să introduceți un comentariu'
COMMENT_MESSAGE_URL: 'Vă rugăm să introduceți un URL valid'
EMAILADDRESS_MESSAGE_EMAIL: 'Vă rugăm să introduceți uo adresă de e-mail validă'
EMAILADDRESS_MESSAGE_REQUIRED: 'Vă rugăm să vă introduceți adresa de e-mail'
POST: Publică
PREVIEW: 'Vizualizare înaintea publicării'
PREVIEWLABEL: 'Vizualizare înaintea publicării'
YOURNAME: 'Numele dvs.'
YOURNAME_MESSAGE_REQUIRED: 'Vă rugăm să vă introduceți numele'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Comentariul dvs. a fost trimis și așteaptă să fie moderat.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Comentariul dvs. a fost trimis și așteaptă să fie moderat.'
COMMENTLOGINERROR: 'Publicarea de comentarii necesită autentificare pe site'
COMMENTPERMISSIONERROR: 'și că aveți nivelul de permisiune corespunzător'
COMMENTPOSTLOGIN: Autentificare
COMMENTS: Comentarii
COMMENTSDISABLED: 'Postarea de comentarii a fost dezactivată'
DELETEALLCOMMENTS: 'Ştergeţi toate comentariile de pe această pagină'
LOGINTOPOSTCOMMENT: 'Autentificaţi-vă pentru a publica un comentariu'
NEXT: următorul
NOCOMMENTSYET: 'Nimeni nu a comentat încă pe aceasta pagină.'
POSTCOM: 'Publicaţi-vă comentariul'
PREV: anteriorul
RSSFEEDALLCOMMENTS: 'RSS pentru toate comentariile'
RSSFEEDCOMMENTS: 'RSS pentru comentariile de pe această pagină'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Commentarii
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Commentarii
EMAILADDRESS: 'Adresa dvs. de e-mail (nu va fi publicată)'
PERMISSIONFAILURE: 'Nu puteţi publica comentarii pe această pagină. Vă rugăm să vă asiguaţi că sunteți autentificat și că aveşi nivelul de permisiune corespunzător.'
RSSTITLE: 'RSS feed pentru comentarii'
WEBSITEURL: 'URL Site web'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Comentarii
COMMENTSTABSET: Comentarii
SilverStripe\Comments\Model\Comment:
COMMENT: Comentariu
COMMENTBY: 'Comentat de %s'
CREATED: 'Data publicării'
EMAIL: E-mail
ISSPAM: 'Spam?'
MODERATED: 'Moderare?'
'ON': pe
PARENTTITLE: Părinte
PLURALNAME: Comentarii
SINGULARNAME: Comentariu
URL: URL

84
lang/ru.yml Normal file
View File

@ -0,0 +1,84 @@
ru:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Введите ваш комментарий'
COMMENT_MESSAGE_URL: 'Пожалуйста, задайте действительный URL-адрес'
EMAILADDRESS_MESSAGE_EMAIL: 'Пожалуйста, укажите действительный электронный адрес'
EMAILADDRESS_MESSAGE_REQUIRED: 'Пожалуйста, укажите свой электронный адрес'
POST: Отправить
PREVIEW: Просмотр
PREVIEWLABEL: Просмотр
YOURNAME: Имя
YOURNAME_MESSAGE_REQUIRED: 'Пожалуйста, укажите своё имя'
Comments:
COMMENTSREQUIRELOGIN: 'Необходимо войти в систему для комментирования'
CommentsAdmin:
Comments: Одобрен
NewComments: Новые
SpamComments: Спам
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Ваш комментарий отправлен и ожидает проверки.'
CommentsInterface_singlecomment_ss:
REPLYTO: Ответить
CommentsInterface_ss:
AWAITINGMODERATION: 'Ваш комментарий отправлен и ожидает проверки.'
COMMENTLOGINERROR: 'Вы не можете отправлять комментарии пока не войдёте в систему'
COMMENTPERMISSIONERROR: 'и пока не получите соответствующие права доступа'
COMMENTPOSTLOGIN: 'Вход в систему'
COMMENTS: Комментарии
COMMENTSDISABLED: 'Комментарии отключены'
DELETEALLCOMMENTS: 'удалить все комментарии со страницы'
LOGINTOPOSTCOMMENT: 'Войдите чтобы получить возможность отправлять комментарии'
NEXT: следующий
NOCOMMENTSYET: 'Комментариев на этой странице пока нет.'
POSTCOM: 'Отправить комментарий'
PREV: предыдущие
RSSFEEDALLCOMMENTS: 'RSS лента для всех комментариев'
RSSFEEDCOMMENTS: 'RSS лента для комментариев на этой странице'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Доступ к разделу ''Комментарии'''
ApprovedComments: Одобренные
Comments: Одобрен
MENUTITLE: Комментарии
NewComments: Новый
SpamComments: Спам
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Одобрить
COMMENTAPPROVED: 'Комментарий одобрен.'
COMMENTMARKEDSPAM: 'Комментарий помечен как спам.'
SPAM: Спам
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Одобрить
COMMENTAPPROVED: 'Комментарий одобрен.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Одобрить
SPAM: Спам
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Комментарий помечен как спам.'
SPAM: Спам
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Комментарии
EMAILADDRESS: 'Ваш электронный адрес (не будет опубликован)'
PERMISSIONFAILURE: 'Вы не можете отправлять комментарии на эту страницу. Убедитесь что вы вошли в систему и имеете необходимые права доступа.'
RSSTITLE: 'RSS-лента комментариев'
WEBSITEURL: 'Адрес вашего сайта'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Комментарии
COMMENTSTABSET: Комментарии
MODERATIONREQUIRED_NONE: 'Проверка не требуется'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Проверка требуется только для незарегистрированных пользователей'
MODERATIONREQUIRED_REQUIRED: 'Проверка требуется для всех комментариев'
SilverStripe\Comments\Model\Comment:
COMMENT: Комментарий
COMMENTBY: 'Комментарий автора %s'
CREATED: 'Дата добавления'
EMAIL: Email
ISSPAM: 'Спам?'
MODERATED: 'Проверено?'
'ON': на
OPTIONS: Опции
OPTION_DESCRIPTION: 'Комментарии не будут отображаться пока не будут одобрены модератором'
PARENTTITLE: Родительская
PLURALNAME: Комментарии
ParentComment_Title: 'Этот комментарий ответ на сообщение ниже'
SINGULARNAME: Комментарий
URL: URL

84
lang/sk.yml Normal file
View File

@ -0,0 +1,84 @@
sk:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Prosím vložte komentár'
COMMENT_MESSAGE_URL: 'Prosím zadajte platnú URL adresu'
EMAILADDRESS_MESSAGE_EMAIL: 'Prosím zadajte platnú e-mailovú adresu'
EMAILADDRESS_MESSAGE_REQUIRED: 'Prosím zadajte váš e-mail'
POST: Odoslať
PREVIEW: Náhľad
PREVIEWLABEL: Náhľad
YOURNAME: 'Vaše meno'
YOURNAME_MESSAGE_REQUIRED: 'Vložte vaše meno'
Comments:
COMMENTSREQUIRELOGIN: 'Vyžadovať prihlásenie na pridávanie komentárov'
CommentsAdmin:
Comments: Schválené
NewComments: Nové
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Váš komentár bol odoslaný a čaká na kontrolu.'
CommentsInterface_singlecomment_ss:
REPLYTO: Odpovedať
CommentsInterface_ss:
AWAITINGMODERATION: 'Váš komentár bol odoslaný a čaká na kontrolu.'
COMMENTLOGINERROR: 'Nemôžete pridávať komentáre pokiaľ nie ste prihlásený'
COMMENTPERMISSIONERROR: 'a nemáte dostatočné oprávnenia'
COMMENTPOSTLOGIN: 'Prihlásiť sa'
COMMENTS: Komentáre
COMMENTSDISABLED: 'Už nie je možné pridávať nové komentáre'
DELETEALLCOMMENTS: 'Odstrániť všetky komentáre na tejto stránke'
LOGINTOPOSTCOMMENT: 'Pre komentovanie sa prihláste'
NEXT: nasledujúce
NOCOMMENTSYET: 'Táto stránka ešte nebola komentovaná.'
POSTCOM: 'Napíšte komentárPridať komentár'
PREV: predchádzajúce
RSSFEEDALLCOMMENTS: 'RSS kanál pre všetky komentáre'
RSSFEEDCOMMENTS: 'RSS kanál pre komentáre na tejto stránke'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Prístup k sekcii ''Komentáre'''
ApprovedComments: Schválené
Comments: Schválené
MENUTITLE: Komentáre
NewComments: Nové
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Schváliť
COMMENTAPPROVED: 'Komentár schválený.'
COMMENTMARKEDSPAM: 'Komentár označený ako spam.'
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Schváliť
COMMENTAPPROVED: 'Komentár schválený.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Schváliť
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Komentár označený ako spam.'
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentáre
EMAILADDRESS: 'Váš e-mail (nebude zverejnený)'
PERMISSIONFAILURE: 'Nemôžete písať komentáre k tejto stránke. Prosím skontrolujte či ste prihlásený, a že máte dostatočné oprávnenia.'
RSSTITLE: 'RSS feed komentárov'
WEBSITEURL: 'Vaša web stránka'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentáre
COMMENTSTABSET: Komentáre
MODERATIONREQUIRED_NONE: 'Nevyžadovať odsúhlasenie'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Vyžadovať odsúhlasenie len od neprihlásených užívateľov'
MODERATIONREQUIRED_REQUIRED: 'Odsúhlasiť všetky komentáre'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentár
COMMENTBY: 'Komentáre od %s'
CREATED: 'Dátum odoslania'
EMAIL: E-mail
ISSPAM: 'Spam?'
MODERATED: 'Odsúhlasený?'
'ON': na
OPTIONS: Možnosti
OPTION_DESCRIPTION: 'Neodsúhlasené a spamové komentáre nebudú zobrazené'
PARENTTITLE: 'Komentovaný príspevok'
PLURALNAME: Komentáre
ParentComment_Title: 'Tento komentár je odpoveď na komentár zobrazený nižšie'
SINGULARNAME: Komentár
URL: 'Web adresa'

84
lang/sk_SK.yml Normal file
View File

@ -0,0 +1,84 @@
sk_SK:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Prosím vložte komentár'
COMMENT_MESSAGE_URL: 'Prosím zadajte platnú URL adresu'
EMAILADDRESS_MESSAGE_EMAIL: 'Prosím zadajte platnú e-mailovú adresu'
EMAILADDRESS_MESSAGE_REQUIRED: 'Prosím zadajte váš e-mail'
POST: Odoslať
PREVIEW: Náhľad
PREVIEWLABEL: Náhľad
YOURNAME: 'Vaše meno'
YOURNAME_MESSAGE_REQUIRED: 'Vložte vaše meno'
Comments:
COMMENTSREQUIRELOGIN: 'Vyžadovať prihlásenie na pridávanie komentárov'
CommentsAdmin:
Comments: Schválené
NewComments: Nové
SpamComments: Spam
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Váš komentár bol odoslaný a čaká na kontrolu.'
CommentsInterface_singlecomment_ss:
REPLYTO: Odpovedať
CommentsInterface_ss:
AWAITINGMODERATION: 'Váš komentár bol odoslaný a čaká na kontrolu.'
COMMENTLOGINERROR: 'Nemôžete pridávať komentáre pokiaľ nie ste prihlásený'
COMMENTPERMISSIONERROR: 'a nemáte dostatočné oprávnenia'
COMMENTPOSTLOGIN: 'Prihlásiť sa'
COMMENTS: Komentáre
COMMENTSDISABLED: 'Už nie je možné pridávať nové komentáre'
DELETEALLCOMMENTS: 'Odstrániť všetky komentáre na tejto stránke'
LOGINTOPOSTCOMMENT: 'Prihláste sa, aby ste mohli písať komentáre'
NEXT: nasledujúce
NOCOMMENTSYET: 'Táto stránka ešte nebola komentovaná.'
POSTCOM: 'Napíšte komentár'
PREV: predchádzajúce
RSSFEEDALLCOMMENTS: 'RSS kanál pre všetky komentáre'
RSSFEEDCOMMENTS: 'RSS kanál pre komentáre na tejto stránke'
SilverStripe\Comments\Admin\CommentAdmin:
ADMIN_PERMISSION: 'Prístup k sekcii ''Komentáre'''
ApprovedComments: Schválené
Comments: Schválené
MENUTITLE: Komentáre
NewComments: Nové
SpamComments: Spam
SilverStripe\Comments\Admin\CommentsGridFieldAction:
APPROVE: Schváliť
COMMENTAPPROVED: 'Komentár schválený.'
COMMENTMARKEDSPAM: 'Komentár označený ako spam.'
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldApproveAction:
APPROVE: Schváliť
COMMENTAPPROVED: 'Komentár schválený.'
SilverStripe\Comments\Admin\CommentsGridFieldConfig:
APPROVE: Schváliť
SPAM: Spam
SilverStripe\Comments\Admin\CommentsGridFieldSpamAction:
COMMENTMARKEDSPAM: 'Komentár označený ako spam.'
SPAM: Spam
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentáre
EMAILADDRESS: 'Váš e-mail (nebude zverejnený)'
PERMISSIONFAILURE: 'Nemôžete písať komentáre k tejto stránke. Prosím skontrolujte či ste prihlásený, a že máte dostatočné oprávnenia.'
RSSTITLE: 'RSS feed komentárov'
WEBSITEURL: 'Vaša web stránka'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentáre
COMMENTSTABSET: Komentáre
MODERATIONREQUIRED_NONE: 'Nevyžadovať odsúhlasenie'
MODERATIONREQUIRED_NONMEMBERSONLY: 'Vyžadovať odsúhlasenie len od neprihlásených užívateľov'
MODERATIONREQUIRED_REQUIRED: 'Odsúhlasiť všetky komentáre'
SilverStripe\Comments\Model\Comment:
COMMENT: Komentár
COMMENTBY: 'Komentáre od %s'
CREATED: 'Dátum zverejnenia'
EMAIL: E-mail
ISSPAM: 'Spam?'
MODERATED: 'Odsúhlasený?'
'ON': na
OPTIONS: Možnosti
OPTION_DESCRIPTION: 'Neodsúhlasené a spamové komentáre nebudú zobrazené'
PARENTTITLE: Rodič
PLURALNAME: Komentáre
ParentComment_Title: 'Tento komentár je odpoveď na komentár zobrazený nižšie'
SINGULARNAME: Komentár
URL: 'Web adresa'

17
lang/sl.yml Normal file
View File

@ -0,0 +1,17 @@
sl:
CommentInterface:
PREVIEW: Predogled
PREVIEWLABEL: Predogled
CommentsInterface_ss:
COMMENTS: Komentarji
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentarji
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentarji
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentarji
COMMENTSTABSET: Komentarji
SilverStripe\Comments\Model\Comment:
EMAIL: E-pošta
PLURALNAME: Komentarji
URL: URL

18
lang/sr.yml Normal file
View File

@ -0,0 +1,18 @@
sr:
CommentInterface:
PREVIEW: 'Претходни преглед'
PREVIEWLABEL: 'Претходни преглед'
CommentsInterface_ss:
COMMENTS: Коментари
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Коментари
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Коментари
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Коментари
COMMENTSTABSET: Коментари
SilverStripe\Comments\Model\Comment:
EMAIL: Е-пошта
'ON': у
PLURALNAME: Коментари
URL: URL

50
lang/sr@latin.yml Normal file
View File

@ -0,0 +1,50 @@
sr@latin:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Molimo unesite komentar'
COMMENT_MESSAGE_URL: 'Molimo Vas da unesete ispravan URL'
EMAILADDRESS_MESSAGE_EMAIL: 'Molimo Vas da unesete ispravnu adresu elektronske pošte'
EMAILADDRESS_MESSAGE_REQUIRED: 'Molimo vas da unesete adresu elektronske pošte'
POST: Objavljivanje
PREVIEW: Prikaz
PREVIEWLABEL: Prikaz
YOURNAME: 'Vaše ime'
YOURNAME_MESSAGE_REQUIRED: 'Molimo Vas da unesete Vaše ime'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Vaš komentar je prosleđen i sada čeka uređivanje.'
CommentsInterface_ss:
AWAITINGMODERATION: 'Vaš komentar je prosleđen i sada čeka uređivanje.'
COMMENTLOGINERROR: 'Ne možete objavljivati komentare dok se ne prijavite'
COMMENTPERMISSIONERROR: 'i da imate odgovarajući nivo ovlašćenja'
COMMENTPOSTLOGIN: 'Prijavite se ovde'
COMMENTS: Komentari
COMMENTSDISABLED: 'Objavljivanje komentara je onemogućeno'
DELETEALLCOMMENTS: 'Obriši sve komentare na ovoj strani'
LOGINTOPOSTCOMMENT: 'Prijavite se da biste objavili komentar'
NEXT: sledeći
NOCOMMENTSYET: 'Niko još uvek nije objavio komentar na ovoj strani'
POSTCOM: 'Objavite Vaš komentar'
PREV: prethodni
RSSFEEDALLCOMMENTS: 'RSS feed za sve komentare'
RSSFEEDCOMMENTS: 'RSS feed za komentare na ovoj strani'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentari
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentari
EMAILADDRESS: 'Vaša adresa elektronske pošte (neće biti objavljeno)'
PERMISSIONFAILURE: 'Niste u mogućnosti da objavljujete komentare na ovoj strani. Molimo Vas da proverite da li ste prijavljeni i da li imate odgovarajući nivo ovlašćenja.'
RSSTITLE: 'RSS Feed komentara'
WEBSITEURL: 'URL Vašeg sajta'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentari
COMMENTSTABSET: Komentari
SilverStripe\Comments\Model\Comment:
COMMENT: Komentar
COMMENTBY: 'Autor komentara %s'
CREATED: 'Datum objavljivanja'
EMAIL: 'Elektronska pošta'
ISSPAM: 'Zlonameran?'
MODERATED: 'Uređen?'
PARENTTITLE: Roditelj
PLURALNAME: Komentari
SINGULARNAME: Komentar
URL: URL

17
lang/sr_RS@latin.yml Normal file
View File

@ -0,0 +1,17 @@
sr_RS@latin:
CommentInterface:
PREVIEW: 'Prethodni pregled'
PREVIEWLABEL: 'Prethodni pregled'
CommentsInterface_ss:
COMMENTS: Komentari
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Komentari
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Komentari
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Komentari
COMMENTSTABSET: Komentari
SilverStripe\Comments\Model\Comment:
EMAIL: E-pošta
PLURALNAME: Komentari
URL: URL

52
lang/sv.yml Normal file
View File

@ -0,0 +1,52 @@
sv:
CommentInterface:
COMMENT_MESSAGE_REQUIRED: 'Var vänlig och skriv in din kommentar'
COMMENT_MESSAGE_URL: 'Var vänlig och ange en korrekt URL'
EMAILADDRESS_MESSAGE_EMAIL: 'Var vänlig och ange an korrekt e-postadress'
EMAILADDRESS_MESSAGE_REQUIRED: 'Var vänlig och skriv in din e-postadress'
POST: Inlägg
PREVIEW: Förhandsgranska
PREVIEWLABEL: Förhandsgranska
YOURNAME: 'Ditt namn'
YOURNAME_MESSAGE_REQUIRED: 'Var vänlig och skriv in ditt namn'
CommentsInterface_pendingcomment_ss:
AWAITINGMODERATION: 'Din kommentar har skickats och väntar nu på moderering'
CommentsInterface_ss:
AWAITINGMODERATION: 'Din kommentar har skickats och väntar nu på moderering'
COMMENTLOGINERROR: 'Du måste logga in för att kunna skriva kommentarer'
COMMENTPERMISSIONERROR: 'och att du har de användarrättigheter som krävs'
COMMENTPOSTLOGIN: 'Logga in'
COMMENTS: Kommentarer
COMMENTSDISABLED: 'Möjligheten att skriva kommentarer har spärrats'
DELETEALLCOMMENTS: 'Radera alla kommenterar på den här sidan'
LOGINTOPOSTCOMMENT: 'Logga in för att skriva en kommentar'
NEXT: nästa
NOCOMMENTSYET: 'Inga har kommenterat på denna sida ännu'
POSTCOM: 'Posta din kommentar'
PREV: föregående
RSSFEEDALLCOMMENTS: 'RSS-flöde för samtliga kommentarer'
RSSFEEDCOMMENTS: 'RSS-flöde för kommentarer på denna sida'
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Kommentarer
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Kommentarer
EMAILADDRESS: 'Din e-postadress (kommer inte att publiceras)'
PERMISSIONFAILURE: 'Du kan inte skriva kommentarer på denna sida. Vänligen kontrollera att du är inloggad och har de användarrättigheter som krävs.'
RSSTITLE: 'RSS-flöde med kommentarer'
WEBSITEURL: 'Din hemsidas URL'
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Kommentarer
COMMENTSTABSET: Kommentarer
SilverStripe\Comments\Model\Comment:
COMMENT: Kommentarer
COMMENTBY: 'Kommentar av %s'
CREATED: Datum
EMAIL: E-post
ISSPAM: 'Spam?'
MODERATED: 'Modererad?'
'ON': den
OPTIONS: Alternativ
PARENTTITLE: 'Överordnad sida'
PLURALNAME: Kommentarer
SINGULARNAME: Kommentar
URL: URL

16
lang/th.yml Normal file
View File

@ -0,0 +1,16 @@
th:
CommentInterface:
PREVIEW: ดูตัวอย่าง
PREVIEWLABEL: ดูตัวอย่าง
CommentsInterface_ss:
COMMENTS: ความคิดเห็น
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: ความคิดเห็น
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: ความคิดเห็น
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: ความคิดเห็น
COMMENTSTABSET: ความคิดเห็น
SilverStripe\Comments\Model\Comment:
EMAIL: อีเมล
PLURALNAME: ความคิดเห็น

17
lang/tr.yml Normal file
View File

@ -0,0 +1,17 @@
tr:
CommentInterface:
PREVIEW: Önizleme
PREVIEWLABEL: Önizleme
CommentsInterface_ss:
COMMENTS: Yorumlar
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: Yorumlar
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: Yorumlar
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: Yorumlar
COMMENTSTABSET: Yorumlar
SilverStripe\Comments\Model\Comment:
EMAIL: Eposta
'ON': üzerinde
PLURALNAME: Yorumlar

21
lang/zh.yml Normal file
View File

@ -0,0 +1,21 @@
zh:
CommentInterface:
PREVIEW: 预览
PREVIEWLABEL: 预览
CommentsInterface_ss:
COMMENTS: 评论
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: 评论
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: 评论
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: 评论
COMMENTSTABSET: 评论
SilverStripe\Comments\Model\Comment:
COMMENT: 评论
EMAIL: 电子邮件
OPTIONS: 选项
PARENTTITLE:
PLURALNAME: 评论
SINGULARNAME: 评论
URL: 网址

14
lang/zh_TW.yml Normal file
View File

@ -0,0 +1,14 @@
zh_TW:
CommentsInterface_ss:
COMMENTS: 意見
SilverStripe\Comments\Admin\CommentAdmin:
MENUTITLE: 意見
SilverStripe\Comments\Controllers\CommentingController:
COMMENTS: 意見
SilverStripe\Comments\Extensions\CommentsExtension:
COMMENTOPTIONS: 意見
COMMENTSTABSET: 意見
SilverStripe\Comments\Model\Comment:
EMAIL: 電子郵件
PLURALNAME: 意見
URL: 網址

12
license.md Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) 2016, SilverStripe Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

62
package.json Normal file
View File

@ -0,0 +1,62 @@
{
"name": "silverstripe-comments",
"version": "1.0.0",
"description": "SilverStripe campaign admin interface",
"directories": {
"test": "tests"
},
"engines": {
"node": "^10.x"
},
"scripts": {
"build": "yarn && NODE_ENV=production webpack -p --bail --progress",
"watch": "yarn && NODE_ENV=development webpack --watch --progress",
"css": "yarn && WEBPACK_CHILD=css webpack -p --bail --progress",
"test": "true",
"coverage": "jest --coverage",
"lint": "eslint client/src && sass-lint -v client/src"
},
"repository": {
"type": "git",
"url": "git://github.com/silverstripe/silverstripe-comments.git"
},
"keywords": [
"silverstripe",
"admin"
],
"author": "SilverStripe Ltd",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/silverstripe/silverstripe-comments/issues"
},
"homepage": "https://github.com/silverstripe/silverstripe-comments",
"dependencies": {
"jquery": "^3.6.0",
"jquery-validation": "^1.19.3"
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.0.6",
"@silverstripe/webpack-config": "^1.3",
"babel-jest": "^19.0.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-es2016": "^6.24.1",
"babel-preset-react": "^6.5.0",
"copy-webpack-plugin": "^4",
"jest-cli": "^19.0.2",
"node-dir": "^0.1.17",
"react-addons-test-utils": "^15.3.1",
"redux-logger": "^2.6.1",
"redux-mock-store": "^1.2.3",
"redux-thunk": "^2.2.0"
},
"babel": {
"presets": [
"react",
"es2015",
"es2016"
],
"plugins": [
"transform-object-rest-spread"
]
}
}

13
phpcs.xml.dist Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<!-- base rules are PSR-2 -->
<rule ref="PSR2" >
<!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" />
</rule>
</ruleset>

16
phpunit.xml.dist Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Default">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

149
src/Admin/CommentAdmin.php Normal file
View File

@ -0,0 +1,149 @@
<?php
namespace SilverStripe\Comments\Admin;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\Tab;
use SilverStripe\Forms\TabSet;
use SilverStripe\Security\PermissionProvider;
use SilverStripe\Security\Security;
/**
* Comment administration system within the CMS
*
* @package comments
*/
class CommentAdmin extends LeftAndMain implements PermissionProvider
{
private static $url_segment = 'comments';
private static $url_rule = '/$Action';
private static $menu_title = 'Comments';
private static $menu_icon_class = 'font-icon-comment';
private static $allowed_actions = [
'approvedmarked',
'deleteall',
'deletemarked',
'hammarked',
'showtable',
'spammarked',
'EditForm',
'unmoderated'
];
private static $required_permission_codes = 'CMS_ACCESS_CommentAdmin';
public function providePermissions()
{
return [
'CMS_ACCESS_CommentAdmin' => [
'name' => _t(__CLASS__ . '.ADMIN_PERMISSION', "Access to 'Comments' section"),
'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
],
];
}
/**
* @return Form
*/
public function getEditForm($id = null, $fields = null)
{
if (!$id) {
$id = $this->currentPageID();
}
$form = parent::getEditForm($id);
$record = $this->getRecord($id);
if ($record && !$record->canView()) {
return Security::permissionFailure($this);
}
$newComments = Comment::get()->filter('Moderated', 0);
$newGrid = CommentsGridField::create(
'NewComments',
'',
$newComments,
CommentsGridFieldConfig::create()
);
$approvedComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 0);
$approvedGrid = CommentsGridField::create(
'ApprovedComments',
'',
$approvedComments,
CommentsGridFieldConfig::create()
);
$spamComments = Comment::get()->filter('Moderated', 1)->filter('IsSpam', 1);
$spamGrid = CommentsGridField::create(
'SpamComments',
'',
$spamComments,
CommentsGridFieldConfig::create()
);
$fields = FieldList::create(
$root = TabSet::create(
'Root',
Tab::create(
'NewComments',
_t(
__CLASS__.'.NewComments',
'New ({count})',
['count' => count($newComments ?? [])]
),
$newGrid
),
Tab::create(
'ApprovedComments',
_t(
__CLASS__.'.ApprovedComments',
'Approved ({count})',
['count' => count($approvedComments ?? [])]
),
$approvedGrid
),
Tab::create(
'SpamComments',
_t(
__CLASS__.'.SpamComments',
'Spam ({count})',
['count' => count($spamComments ?? [])]
),
$spamGrid
)
)
);
$actions = FieldList::create();
$form = Form::create(
$this,
'EditForm',
$fields,
$actions
);
$form->addExtraClass('cms-edit-form fill-height');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
if ($form->Fields()->hasTabset()) {
$form->Fields()->findOrMakeTab('Root')->setTemplate('SilverStripe\\Forms\\CMSTabSet');
$form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses());
}
$this->extend('updateEditForm', $form);
return $form;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace SilverStripe\Comments\Admin;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\ORM\SS_List;
use SilverStripe\View\HTML;
class CommentsGridField extends GridField
{
/**
* @param string $name
* @param string $title
* @param SS_List $dataList
* @param GridFieldConfig $config
*/
public function __construct($name, $title = null, SS_List $dataList = null, GridFieldConfig $config = null)
{
parent::__construct($name, $title, $dataList, $config);
$this->addExtraClass('grid-field__filter-buttons');
}
/**
* {@inheritdoc}
*/
protected function newRow($total, $index, $record, $attributes, $content)
{
if (!isset($attributes['class'])) {
$attributes['class'] = '';
}
if ($record->IsSpam) {
$attributes['class'] .= ' spam';
}
return HTML::createTag(
'tr',
$attributes,
$content
);
}
}

View File

@ -0,0 +1,138 @@
<?php
namespace SilverStripe\Comments\Admin;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridField_ActionProvider;
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
use SilverStripe\Forms\GridField\GridField_FormAction;
/**
* @deprecated 3.2.0 Use CommentsGridFieldApproveAction CommentsGridFieldSpamAction instead
*/
class CommentsGridFieldAction implements GridField_ColumnProvider, GridField_ActionProvider
{
/**
* {@inheritdoc}
*/
public function __construct()
{
Deprecation::notice(
'3.2.0',
'Use CommentsGridFieldApproveAction CommentsGridFieldSpamAction instead',
Deprecation::SCOPE_CLASS
);
}
public function augmentColumns($gridField, &$columns)
{
if (!in_array('Actions', $columns ?? [])) {
$columns[] = 'Actions';
}
}
/**
* {@inheritdoc}
*/
public function getColumnAttributes($gridField, $record, $columnName)
{
return ['class' => 'col-buttons'];
}
/**
* {@inheritdoc}
*/
public function getColumnMetadata($gridField, $columnName)
{
if ($columnName === 'Actions') {
return ['title' => ''];
}
}
/**
* {@inheritdoc}
*/
public function getColumnsHandled($gridField)
{
return ['Actions'];
}
/**
* {@inheritdoc}
*/
public function getColumnContent($gridField, $record, $columnName)
{
if (!$record->canEdit()) {
return;
}
$field = '';
if (!$record->IsSpam || !$record->Moderated) {
$field .= GridField_FormAction::create(
$gridField,
'CustomAction' . $record->ID . 'Spam',
_t(__CLASS__ . '.SPAM', 'Spam'),
'spam',
['RecordID' => $record->ID]
)
->addExtraClass('btn btn-secondary grid-field__icon-action')
->Field();
}
if ($record->IsSpam || !$record->Moderated) {
$field .= GridField_FormAction::create(
$gridField,
'CustomAction' . $record->ID . 'Approve',
_t(__CLASS__ . '.APPROVE', 'Approve'),
'approve',
['RecordID' => $record->ID]
)
->addExtraClass('btn btn-secondary grid-field__icon-action')
->Field();
}
return $field;
}
/**
* {@inheritdoc}
*/
public function getActions($gridField)
{
return ['spam', 'approve'];
}
/**
* {@inheritdoc}
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
if ($actionName === 'spam') {
/** @var Comment $comment */
$comment = Comment::get()->byID($arguments['RecordID']);
$comment->markSpam();
// output a success message to the user
Controller::curr()->getResponse()->setStatusCode(
200,
_t(__CLASS__ . '.COMMENTMARKEDSPAM', 'Comment marked as spam.')
);
}
if ($actionName === 'approve') {
/** @var Comment $comment */
$comment = Comment::get()->byID($arguments['RecordID']);
$comment->markApproved();
// output a success message to the user
Controller::curr()->getResponse()->setStatusCode(
200,
_t(__CLASS__ . '.COMMENTAPPROVED', 'Comment approved.')
);
}
}
}

View File

@ -0,0 +1,140 @@
<?php
namespace SilverStripe\Comments\Admin;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridField_ActionMenuItem;
use SilverStripe\Forms\GridField\GridField_ActionProvider;
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
use SilverStripe\Forms\GridField\GridField_FormAction;
class CommentsGridFieldApproveAction implements
GridField_ColumnProvider,
GridField_ActionProvider,
GridField_ActionMenuItem
{
/**
* {@inheritdoc}
*/
public function augmentColumns($gridField, &$columns)
{
if (!in_array('Actions', $columns ?? [])) {
$columns[] = 'Actions';
}
}
public function getTitle($gridField, $record, $columnName)
{
return _t(__CLASS__ . '.APPROVE', 'Approve');
}
public function getExtraData($gridField, $record, $columnName)
{
$field = $this->getApproveAction($gridField, $record, $columnName);
if ($field) {
return $field->getAttributes();
}
return null;
}
public function getGroup($gridField, $record, $columnName)
{
$field = $this->getApproveAction($gridField, $record, $columnName);
return $field ? GridField_ActionMenuItem::DEFAULT_GROUP: null;
}
/**
* {@inheritdoc}
*/
public function getColumnAttributes($gridField, $record, $columnName)
{
return ['class' => 'col-buttons grid-field__col-compact'];
}
/**
* {@inheritdoc}
*/
public function getColumnMetadata($gridField, $columnName)
{
if ($columnName === 'Actions') {
return ['title' => ''];
}
}
/**
* {@inheritdoc}
*/
public function getColumnsHandled($gridField)
{
return ['Actions'];
}
/**
* {@inheritdoc}
*/
public function getColumnContent($gridField, $record, $columnName)
{
if (!$record->canEdit()) {
return;
}
$field = $this->getApproveAction($gridField, $record, $columnName);
return $field ? $field->Field() : null;
}
/**
* Returns the FormAction object, used by other methods to get properties
*
* @return GridField_FormAction|null
*/
public function getApproveAction($gridField, $record, $columnName)
{
$field = GridField_FormAction::create(
$gridField,
'CustomAction' . $record->ID . 'Approve',
_t(__CLASS__ . '.APPROVE', 'Approve'),
'approve',
['RecordID' => $record->ID]
)
->addExtraClass(implode(' ', [
'btn',
'btn-secondary',
'grid-field__icon-action',
'action-menu--handled',
'font-icon-check-mark',
]))
->setAttribute('classNames', 'font-icon-check-mark');
return ($record->IsSpam || !$record->Moderated) ? $field : null;
}
/**
* {@inheritdoc}
*/
public function getActions($gridField)
{
return ['approve'];
}
/**
* {@inheritdoc}
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
/** @var Comment $comment */
$comment = Comment::get()->byID($arguments['RecordID']);
$comment->markApproved();
// output a success message to the user
Controller::curr()->getResponse()->setStatusCode(
200,
_t(__CLASS__ . '.COMMENTAPPROVED', 'Comment approved.')
);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace SilverStripe\Comments\Admin\CommentsGridFieldBulkAction;
use SilverStripe\Comments\Model\Comment;
/**
* A {@link Handler} for bulk approving comments
*/
class ApproveHandler extends CommentHandler
{
private static $url_segment = 'approve';
protected $buttonClasses = 'font-icon-tick';
protected $label = 'Approve';
/**
* @param Comment $comment
*
* @return Comment
*/
public function updateComment($comment)
{
$comment->markApproved();
return $comment;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace SilverStripe\Comments\Admin\CommentsGridFieldBulkAction;
use Colymba\BulkManager\BulkAction\Handler;
use Colymba\BulkTools\HTTPBulkToolsResponse;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Core\Convert;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
abstract class CommentHandler extends Handler
{
protected $xhr = true;
protected $destructive = false;
/**
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function index(HTTPRequest $request)
{
$ids = [];
$response = new HTTPBulkToolsResponse(
true,
$this->gridField,
200
);
foreach ($this->getRecords() as $comment) {
array_push($ids, $comment->ID);
$this->updateComment($comment);
$response->addSuccessRecord($comment);
}
$response->setMessage(_t(__CLASS__ . '.CHANGES_APPLIED', 'Changes applied'));
return $response;
}
/**
* @param Comment $comment
*
* @return Comment
*/
abstract public function updateComment($comment);
}

View File

@ -0,0 +1,78 @@
<?php
namespace SilverStripe\Comments\Admin\CommentsGridFieldBulkAction;
use SilverStripe\Dev\Deprecation;
use Colymba\BulkManager\BulkAction\Handler as GridFieldBulkActionHandler;
use SilverStripe\Core\Convert;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
/**
* A {@link GridFieldBulkActionHandler} for bulk marking comments as spam
*
* @deprecated 3.1.0 Use concrete Spam or Approve handlers instead
*/
class Handler extends GridFieldBulkActionHandler
{
private static $allowed_actions = array(
'spam',
'approve',
);
private static $url_handlers = array(
'spam' => 'spam',
'approve' => 'approve',
);
/**
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function __construct()
{
Deprecation::notice('3.1.0', 'Use concrete Spam or Approve handlers instead', Deprecation::SCOPE_CLASS);
}
public function spam(HTTPRequest $request)
{
$ids = array();
foreach ($this->getRecords() as $record) {
array_push($ids, $record->ID);
$record->markSpam();
}
$response = new HTTPResponse(json_encode(array(
'done' => true,
'records' => $ids
)));
$response->addHeader('Content-Type', 'text/json');
return $response;
}
/**
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function approve(HTTPRequest $request)
{
$ids = array();
foreach ($this->getRecords() as $record) {
array_push($ids, $record->ID);
$record->markApproved();
}
$response = new HTTPResponse(json_encode(array(
'done' => true,
'records' => $ids
)));
$response->addHeader('Content-Type', 'text/json');
return $response;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace SilverStripe\Comments\Admin\CommentsGridFieldBulkAction;
use SilverStripe\Comments\Model\Comment;
/**
* A {@link Handler} for bulk marking comments as spam
*/
class SpamHandler extends CommentHandler
{
private static $url_segment = 'spam';
protected $buttonClasses = 'font-icon-cross-mark';
protected $label = 'Spam';
/**
* @param Comment $comment
*
* @return Comment
*/
public function updateComment($comment)
{
$comment->markSpam();
return $comment;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace SilverStripe\Comments\Admin;
use Colymba\BulkManager\BulkManager;
use SilverStripe\Comments\Admin\CommentsGridFieldBulkAction\ApproveHandler;
use SilverStripe\Comments\Admin\CommentsGridFieldBulkAction\SpamHandler;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\GridField\GridField_ActionMenu;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
class CommentsGridFieldConfig extends GridFieldConfig_RecordEditor
{
public function __construct($itemsPerPage = 25)
{
parent::__construct($itemsPerPage);
// $this->addComponent(new GridFieldExportButton());
$this->addComponents([
new CommentsGridFieldSpamAction(),
new CommentsGridFieldApproveAction(),
]);
// Format column
/** @var GridFieldDataColumns $columns */
$columns = $this->getComponentByType(GridFieldDataColumns::class);
$columns->setFieldFormatting([
'Parent.Title' => function ($value, &$item) {
if ($link = $item->Link()) {
return sprintf(
'<a href="%s" class="cms-panel-link external-link action" target="_blank">%s</a>',
Convert::raw2att($link),
$item->obj('ParentTitle')->forTemplate()
);
} else {
return $item->obj('ParentTitle')->forTemplate();
}
}
]);
// Add bulk option
$manager = BulkManager::create(null, false);
$spamAction = SpamHandler::create()->setLabel(_t(__CLASS__ . '.SPAM', 'Spam'));
$approveAction = ApproveHandler::create()->setLabel(_t(__CLASS__ . '.APPROVE', 'Approve'));
$manager
->addBulkAction($spamAction)
->addBulkAction($approveAction);
$this->addComponent($manager);
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace SilverStripe\Comments\Admin;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridField_ActionMenuItem;
use SilverStripe\Forms\GridField\GridField_ActionProvider;
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
use SilverStripe\Forms\GridField\GridField_FormAction;
class CommentsGridFieldSpamAction implements
GridField_ColumnProvider,
GridField_ActionProvider,
GridField_ActionMenuItem
{
/**
* {@inheritdoc}
*/
public function augmentColumns($gridField, &$columns)
{
if (!in_array('Actions', $columns ?? [])) {
$columns[] = 'Actions';
}
}
public function getTitle($gridField, $record, $columnName)
{
return _t(__CLASS__ . '.SPAM', 'Spam');
}
public function getExtraData($gridField, $record, $columnName)
{
$field = $this->getSpamAction($gridField, $record, $columnName);
if ($field) {
return $field->getAttributes();
}
return null;
}
public function getGroup($gridField, $record, $columnName)
{
$field = $this->getSpamAction($gridField, $record, $columnName);
return $field ? GridField_ActionMenuItem::DEFAULT_GROUP: null;
}
/**
* {@inheritdoc}
*/
public function getColumnAttributes($gridField, $record, $columnName)
{
return ['class' => 'col-buttons grid-field__col-compact'];
}
/**
* {@inheritdoc}
*/
public function getColumnMetadata($gridField, $columnName)
{
if ($columnName === 'Actions') {
return ['title' => ''];
}
}
/**
* {@inheritdoc}
*/
public function getColumnsHandled($gridField)
{
return ['Actions'];
}
/**
* {@inheritdoc}
*/
public function getColumnContent($gridField, $record, $columnName)
{
if (!$record->canEdit()) {
return;
}
$field = $this->getSpamAction($gridField, $record, $columnName);
return $field ? $field->Field() : null;
}
/**
* Returns the FormAction object, used by other methods to get properties
*
* @return GridField_FormAction|null
*/
public function getSpamAction($gridField, $record, $columnName)
{
$field = GridField_FormAction::create(
$gridField,
'CustomAction' . $record->ID . 'Spam',
_t(__CLASS__ . '.SPAM', 'Spam'),
'spam',
['RecordID' => $record->ID]
)
->addExtraClass(implode(' ', [
'btn',
'btn-secondary',
'grid-field__icon-action',
'action-menu--handled',
'font-icon-cross-mark',
]))
->setAttribute('classNames', 'font-icon-cross-mark');
return (!$record->IsSpam || !$record->Moderated) ? $field : null;
}
/**
* {@inheritdoc}
*/
public function getActions($gridField)
{
return ['spam'];
}
/**
* {@inheritdoc}
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
/** @var Comment $comment */
$comment = Comment::get()->byID($arguments['RecordID']);
$comment->markSpam();
// output a success message to the user
Controller::curr()->getResponse()->setStatusCode(
200,
_t(__CLASS__ . '.COMMENTMARKEDSPAM', 'Comment marked as spam.')
);
}
}

View File

@ -0,0 +1,534 @@
<?php
namespace SilverStripe\Comments\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Comments\Extensions\CommentsExtension;
use SilverStripe\Comments\Forms\CommentForm;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTP;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\RSS\RSSFeed;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\Form;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Security\Security;
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
/**
* @package comments
*/
class CommentingController extends Controller
{
/**
* {@inheritDoc}
*/
private static $allowed_actions = [
'delete',
'spam',
'ham',
'approve',
'rss',
'CommentsForm',
'reply',
'doPostComment',
'doPreviewComment',
];
/**
* {@inheritDoc}
*/
private static $url_handlers = [
'reply/$ParentCommentID//$ID/$OtherID' => 'reply',
];
/**
* Fields required for this form
*
* @var array
* @config
*/
private static $required_fields = [
'Name',
'Email',
'Comment',
];
/**
* Parent class this commenting form is for
*
* @var string
*/
private $parentClass = '';
/**
* The record this commenting form is for
*
* @var DataObject
*/
private $ownerRecord;
/**
* Parent controller record
*
* @var Controller
*/
private $ownerController;
/**
* Backup url to return to
*
* @var string
*/
protected $fallbackReturnURL;
/**
* Set the parent class name to use
*
* @param string $class
*/
public function setParentClass($class)
{
$this->parentClass = $this->encodeClassName($class);
}
/**
* Get the parent class name used
*
* @return string
*/
public function getParentClass()
{
return $this->decodeClassName($this->parentClass);
}
/**
* Encode a fully qualified class name to a URL-safe version
*
* @param string $input
* @return string
*/
public function encodeClassName($input)
{
return str_replace('\\', '-', $input ?? '');
}
/**
* Decode an "encoded" fully qualified class name back to its original
*
* @param string $input
* @return string
*/
public function decodeClassName($input)
{
return str_replace('-', '\\', $input ?? '');
}
/**
* Set the record this controller is working on
*
* @param DataObject $record
*/
public function setOwnerRecord($record)
{
$this->ownerRecord = $record;
}
/**
* Get the record
*
* @return DataObject
*/
public function getOwnerRecord()
{
return $this->ownerRecord;
}
/**
* Set the parent controller
*
* @param Controller $controller
*/
public function setOwnerController($controller)
{
$this->ownerController = $controller;
}
/**
* Get the parent controller
*
* @return Controller
*/
public function getOwnerController()
{
return $this->ownerController;
}
/**
* Get the commenting option for the current state
*
* @param string $key
* @return mixed Result if the setting is available, or null otherwise
*/
public function getOption($key)
{
// If possible use the current record
if ($record = $this->getOwnerRecord()) {
/** @var DataObject|CommentsExtension $record */
return $record->getCommentsOption($key);
}
// Otherwise a singleton of that record
if ($class = $this->getParentClass()) {
return singleton($class)->getCommentsOption($key);
}
// Otherwise just use the default options
return singleton(CommentsExtension::class)->getCommentsOption($key);
}
/**
* Returns all the commenting options for the current instance.
*
* @return array
*/
public function getOptions()
{
if ($record = $this->getOwnerRecord()) {
/** @var DataObject|CommentsExtension $record */
return $record->getCommentsOptions();
}
// Otherwise a singleton of that record
if ($class = $this->getParentClass()) {
return singleton($class)->getCommentsOptions();
}
// Otherwise just use the default options
return singleton(CommentsExtension::class)->getCommentsOptions();
}
/**
* Workaround for generating the link to this controller
*
* @param string $action
* @param int $id
* @param string $other
* @return string
*/
public function Link($action = '', $id = '', $other = '')
{
return Controller::join_links(Director::baseURL(), 'comments', $action, $id, $other);
}
/**
* Outputs the RSS feed of comments
*
* @return DBHTMLText
*/
public function rss()
{
return $this->getFeed($this->request)->outputToBrowser();
}
/**
* Return an RSSFeed of comments for a given set of comments or all
* comments on the website.
*
* @param HTTPRequest
*
* @return RSSFeed
*/
public function getFeed(HTTPRequest $request)
{
$link = $this->Link('rss');
$class = $this->decodeClassName($request->param('ID'));
$id = $request->param('OtherID');
// Support old pageid param
if (!$id && !$class && ($id = $request->getVar('pageid'))) {
$class = SiteTree::class;
}
$comments = Comment::get()->filter([
'Moderated' => 1,
'IsSpam' => 0,
]);
// Check if class filter
if ($class) {
if (!is_subclass_of($class, DataObject::class) || !$class::has_extension(CommentsExtension::class)) {
return $this->httpError(404);
}
$this->setParentClass($class);
$comments = $comments->filter('ParentClass', $class);
$link = Controller::join_links($link, $this->encodeClassName($class));
// Check if id filter
if ($id) {
$comments = $comments->filter('ParentID', $id);
$link = Controller::join_links($link, $id);
$this->setOwnerRecord(DataObject::get_by_id($class, $id));
}
}
$title = _t(__CLASS__ . '.RSSTITLE', "Comments RSS Feed");
$comments = PaginatedList::create($comments, $request);
$comments->setPageLength($this->getOption('comments_per_page'));
return RSSFeed::create(
$comments,
$link,
$title,
$link,
'Title',
'EscapedComment',
'AuthorName'
);
}
/**
* Deletes a given {@link Comment} via the URL.
*/
public function delete()
{
$comment = $this->getComment();
if (!$comment) {
return $this->httpError(404);
}
if (!$comment->canDelete()) {
return Security::permissionFailure($this, 'You do not have permission to delete this comment');
}
if (!$comment->getSecurityToken()->checkRequest($this->request)) {
return $this->httpError(400);
}
$comment->delete();
return $this->request->isAjax()
? true
: $this->redirectBack();
}
/**
* Marks a given {@link Comment} as spam. Removes the comment from display
*/
public function spam()
{
$comment = $this->getComment();
if (!$comment) {
return $this->httpError(404);
}
if (!$comment->canEdit()) {
return Security::permissionFailure($this, 'You do not have permission to edit this comment');
}
if (!$comment->getSecurityToken()->checkRequest($this->request)) {
return $this->httpError(400);
}
$comment->markSpam();
return $this->renderChangedCommentState($comment);
}
/**
* Marks a given {@link Comment} as ham (not spam).
*/
public function ham()
{
$comment = $this->getComment();
if (!$comment) {
return $this->httpError(404);
}
if (!$comment->canEdit()) {
return Security::permissionFailure($this, 'You do not have permission to edit this comment');
}
if (!$comment->getSecurityToken()->checkRequest($this->request)) {
return $this->httpError(400);
}
$comment->markApproved();
return $this->renderChangedCommentState($comment);
}
/**
* Marks a given {@link Comment} as approved.
*/
public function approve()
{
$comment = $this->getComment();
if (!$comment) {
return $this->httpError(404);
}
if (!$comment->canEdit()) {
return Security::permissionFailure($this, 'You do not have permission to approve this comment');
}
if (!$comment->getSecurityToken()->checkRequest($this->request)) {
return $this->httpError(400);
}
$comment->markApproved();
return $this->renderChangedCommentState($comment);
}
/**
* Redirect back to referer if available, ensuring that only site URLs
* are allowed to avoid phishing. If it's an AJAX request render the
* comment in it's new state
*
* @param Comment $comment
* @return DBHTMLText|HTTPResponse|false
*/
private function renderChangedCommentState($comment)
{
$referer = $this->request->getHeader('Referer');
// Render comment using AJAX
if ($this->request->isAjax()) {
return $comment->renderWith('Includes/CommentsInterface_singlecomment');
}
// Redirect to either the comment or start of the page
if (empty($referer)) {
return $this->redirectBack();
}
// Redirect to the comment, but check for phishing
$url = $referer . '#comment-' . $comment->ID;
// absolute redirection URLs not located on this site may cause phishing
if (Director::is_site_url($url)) {
return $this->redirect($url);
}
return false;
}
/**
* Returns the comment referenced in the URL (by ID). Permission checking
* should be done in the callee.
*
* @return Comment|false
*/
public function getComment()
{
$id = isset($this->urlParams['ID']) ? $this->urlParams['ID'] : false;
if ($id) {
/** @var Comment $comment */
$comment = Comment::get()->byId($id);
if ($comment) {
$this->fallbackReturnURL = $comment->Link();
return $comment;
}
}
return false;
}
/**
* Create a reply form for a specified comment
*
* @param Comment $comment
* @return Form
*/
public function ReplyForm($comment)
{
// Enables multiple forms with different names to use the same handler
$form = $this->CommentsForm();
$form->setName('ReplyForm_' . $comment->ID);
$form->setHTMLID(null);
$form->addExtraClass('reply-form');
// Load parent into reply form
$form->loadDataFrom([
'ParentCommentID' => $comment->ID
]);
// Customise action
$form->setFormAction($this->Link('reply', $comment->ID));
$this->extend('updateReplyForm', $form);
return $form;
}
/**
* Request handler for reply form.
*
* This method will disambiguate multiple reply forms in the same method
*
* @param HTTPRequest $request
* @throws HTTPResponse_Exception
*/
public function reply(HTTPRequest $request)
{
// Extract parent comment from reply and build this way
if ($parentID = $request->param('ParentCommentID')) {
/** @var Comment $comment */
$comment = DataObject::get_by_id(Comment::class, $parentID, true);
if ($comment) {
return $this->ReplyForm($comment);
}
}
return $this->httpError(404);
}
/**
* Post a comment form
*
* @return Form
*/
public function CommentsForm()
{
$form = Injector::inst()->create(CommentForm::class, __FUNCTION__, $this);
// hook to allow further extensions to alter the comments form
$this->extend('alterCommentForm', $form);
return $form;
}
/**
* @return HTTPResponse|false
*/
public function redirectBack()
{
// Don't cache the redirect back ever
HTTPCacheControlMiddleware::singleton()->setMaxAge(0);
$url = null;
// In edge-cases, this will be called outside of a handleRequest() context; in that case,
// redirect to the homepage - don't break into the global state at this stage because we'll
// be calling from a test context or something else where the global state is inappropraite
if ($this->request) {
if ($this->request->requestVar('BackURL')) {
$url = $this->request->requestVar('BackURL');
} elseif ($this->request->isAjax() && $this->request->getHeader('X-Backurl')) {
$url = $this->request->getHeader('X-Backurl');
} elseif ($this->request->getHeader('Referer')) {
$url = $this->request->getHeader('Referer');
}
}
if (!$url) {
$url = $this->fallbackReturnURL;
}
if (!$url) {
$url = Director::baseURL();
}
// absolute redirection URLs not located on this site may cause phishing
if (Director::is_site_url($url)) {
return $this->redirect($url);
}
return false;
}
}

View File

@ -0,0 +1,631 @@
<?php
namespace SilverStripe\Comments\Extensions;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Comments\Admin\CommentsGridField;
use SilverStripe\Comments\Admin\CommentsGridFieldConfig;
use SilverStripe\Comments\Controllers\CommentingController;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Tab;
use SilverStripe\Forms\TabSet;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
use SilverStripe\View\Requirements;
/**
* Extension to {@link DataObject} to enable tracking comments.
*
* @package comments
*/
class CommentsExtension extends DataExtension
{
/**
* Default configuration values
*
* enabled: Allows commenting to be disabled even if the extension is present
* enabled_cms: Allows commenting to be enabled or disabled via the CMS
* require_login: Boolean, whether a user needs to login (required for required_permission)
* require_login_cms: Allows require_login to be set via the CMS
* required_permission: Permission (or array of permissions) required to comment
* include_js: Enhance operation by ajax behaviour on moderation links (required for use_preview)
* use_gravatar: Set to true to show gravatar icons
* gravatar_default: Theme for 'not found' gravatar {@see http://gravatar.com/site/implement/images}
* gravatar_rating: Gravatar rating (same as the standard default)
* show_comments_when_disabled: Show older comments when commenting has been disabled.
* order_comments_by: Default sort order.
* order_replies_by: Sort order for replies.
* comments_holder_id: ID for the comments holder
* comment_permalink_prefix: ID prefix for each comment
* require_moderation: Require moderation for all comments
* require_moderation_cms: Ignore other comment moderation config settings and set via CMS
* frontend_moderation: Display unmoderated comments in the frontend, if the user can moderate them.
* frontend_spam: Display spam comments in the frontend, if the user can moderate them.
* html_allowed: Allow for sanitized HTML in comments
* use_preview: Preview formatted comment (when allowing HTML)
* nested_comments: Enable nested comments
* nested_depth: Max depth of nested comments in levels (where root is 1 depth) 0 means no limit.
*
* @var array
*
* @config
*/
private static $comments = [
'enabled' => true,
'enabled_cms' => false,
'require_login' => false,
'require_login_cms' => false,
'required_permission' => false,
'include_js' => true,
'use_gravatar' => false,
'gravatar_size' => 80,
'gravatar_default' => 'identicon',
'gravatar_rating' => 'g',
'show_comments_when_disabled' => false,
'order_comments_by' => '"Created" DESC',
'order_replies_by' => false,
'comments_per_page' => 10,
'comments_holder_id' => 'comments-holder',
'comment_permalink_prefix' => 'comment-',
'require_moderation' => false,
'require_moderation_nonmembers' => false,
'require_moderation_cms' => false,
'frontend_moderation' => false,
'frontend_spam' => false,
'html_allowed' => false,
'html_allowed_elements' => ['a', 'img', 'i', 'b'],
'use_preview' => false,
'nested_comments' => false,
'nested_depth' => 2,
];
/**
* @var array
*/
private static $db = [
'ProvideComments' => 'Boolean',
'ModerationRequired' => 'Enum(\'None,Required,NonMembersOnly\',\'None\')',
'CommentsRequireLogin' => 'Boolean',
];
/**
* {@inheritDoc}
*/
private static $has_many = [
'Commments' => Comment::class . '.Parent'
];
/**
* CMS configurable options should default to the config values, but respect
* default values specified by the object
*/
public function populateDefaults()
{
$defaults = $this->owner->config()->get('defaults');
// Set if comments should be enabled by default
if (isset($defaults['ProvideComments'])) {
$this->owner->ProvideComments = $defaults['ProvideComments'];
} else {
$this->owner->ProvideComments = $this->owner->getCommentsOption('enabled') ? 1 : 0;
}
// If moderation options should be configurable via the CMS then
if (isset($defaults['ModerationRequired'])) {
$this->owner->ModerationRequired = $defaults['ModerationRequired'];
} elseif ($this->owner->getCommentsOption('require_moderation')) {
$this->owner->ModerationRequired = 'Required';
} elseif ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
$this->owner->ModerationRequired = 'NonMembersOnly';
} else {
$this->owner->ModerationRequired = 'None';
}
// Set login required
if (isset($defaults['CommentsRequireLogin'])) {
$this->owner->CommentsRequireLogin = $defaults['CommentsRequireLogin'];
} else {
$this->owner->CommentsRequireLogin = $this->owner->getCommentsOption('require_login') ? 1 : 0;
}
}
/**
* If this extension is applied to a {@link SiteTree} record then
* append a Provide Comments checkbox to allow authors to trigger
* whether or not to display comments
*
* @todo Allow customization of other {@link Commenting} configuration
*
* @param FieldList $fields
*/
public function updateSettingsFields(FieldList $fields)
{
$options = FieldGroup::create()->setTitle(_t(__CLASS__ . '.COMMENTOPTIONS', 'Comments'));
// Check if enabled setting should be cms configurable
if ($this->owner->getCommentsOption('enabled_cms')) {
$options->push(CheckboxField::create('ProvideComments', _t(
'SilverStripe\\Comments\\Model\\Comment.ALLOWCOMMENTS',
'Allow comments'
)));
}
// Check if we should require users to login to comment
if ($this->owner->getCommentsOption('require_login_cms')) {
$options->push(
CheckboxField::create(
'CommentsRequireLogin',
_t('Comments.COMMENTSREQUIRELOGIN', 'Require login to comment')
)
);
}
if ($options->FieldList()->count()) {
if ($fields->hasTabSet()) {
$fields->addFieldsToTab('Root.Settings', $options);
} else {
$fields->push($options);
}
}
// Check if moderation should be enabled via cms configurable
if ($this->owner->getCommentsOption('require_moderation_cms')) {
$moderationField = DropdownField::create(
'ModerationRequired',
_t(
__CLASS__ . '.COMMENTMODERATION',
'Comment Moderation'
),
[
'None' => _t(__CLASS__ . '.MODERATIONREQUIRED_NONE', 'No moderation required'),
'Required' => _t(__CLASS__ . '.MODERATIONREQUIRED_REQUIRED', 'Moderate all comments'),
'NonMembersOnly' => _t(
__CLASS__ . '.MODERATIONREQUIRED_NONMEMBERSONLY',
'Only moderate non-members'
),
]
);
if ($fields->hasTabSet()) {
$fields->addFieldToTab('Root.Settings', $moderationField);
} else {
$fields->push($moderationField);
}
}
}
/**
* Get comment moderation rules for this parent
*
* None: No moderation required
* Required: All comments
* NonMembersOnly: Only anonymous users
*
* @return string
*/
public function getModerationRequired()
{
if ($this->owner->getCommentsOption('require_moderation_cms')) {
return $this->owner->getField('ModerationRequired');
}
if ($this->owner->getCommentsOption('require_moderation')) {
return 'Required';
}
if ($this->owner->getCommentsOption('require_moderation_nonmembers')) {
return 'NonMembersOnly';
}
return 'None';
}
/**
* Determine if users must be logged in to post comments
*
* @return boolean
*/
public function getCommentsRequireLogin()
{
if ($this->owner->getCommentsOption('require_login_cms')) {
return (bool) $this->owner->getField('CommentsRequireLogin');
}
return (bool) $this->owner->getCommentsOption('require_login');
}
/**
* Returns the RelationList of all comments against this object. Can be used as a data source
* for a gridfield with write access.
*
* @return DataList
*/
public function AllComments()
{
$order = $this->owner->getCommentsOption('order_comments_by');
$comments = Comment::get()
->filter([
'ParentID' => $this->owner->ID,
'ParentClass' => $this->owner->ClassName,
])
->sort($order);
$this->owner->extend('updateAllComments', $comments);
return $comments;
}
/**
* Returns all comments against this object, with with spam and unmoderated items excluded, for use in the frontend
*
* @return DataList
*/
public function AllVisibleComments()
{
$list = $this->AllComments();
// Filter spam comments for non-administrators if configured
$showSpam = $this->owner->getCommentsOption('frontend_spam') && $this->owner->canModerateComments();
if (!$showSpam) {
$list = $list->filter('IsSpam', 0);
}
// Filter un-moderated comments for non-administrators if moderation is enabled
$showUnmoderated = ($this->owner->ModerationRequired === 'None')
|| ($this->owner->getCommentsOption('frontend_moderation') && $this->owner->canModerateComments());
if (!$showUnmoderated) {
$list = $list->filter('Moderated', 1);
}
$this->owner->extend('updateAllVisibleComments', $list);
return $list;
}
/**
* Returns the root level comments, with spam and unmoderated items excluded, for use in the frontend
*
* @return DataList
*/
public function Comments()
{
$list = $this->AllVisibleComments();
// If nesting comments, only show root level
if ($this->owner->getCommentsOption('nested_comments')) {
$list = $list->filter('ParentCommentID', 0);
}
$this->owner->extend('updateComments', $list);
return $list;
}
/**
* Returns a paged list of the root level comments, with spam and unmoderated items excluded,
* for use in the frontend
*
* @return PaginatedList
*/
public function PagedComments()
{
$list = $this->Comments();
// Add pagination
$list = PaginatedList::create($list, Controller::curr()->getRequest());
$list->setPaginationGetVar('commentsstart' . $this->owner->ID);
$list->setPageLength($this->owner->getCommentsOption('comments_per_page'));
$this->owner->extend('updatePagedComments', $list);
return $list;
}
/**
* Determine if comments are enabled for this instance
*
* @return boolean
*/
public function getCommentsEnabled()
{
// Don't display comments form for pseudo-pages (such as the login form)
if (!$this->owner->exists()) {
return false;
}
// Determine which flag should be used to determine if this is enabled
if ($this->owner->getCommentsOption('enabled_cms')) {
return (bool) $this->owner->ProvideComments;
}
return (bool) $this->owner->getCommentsOption('enabled');
}
/**
* Get the HTML ID for the comment holder in the template
*
* @return string
*/
public function getCommentHolderID()
{
return $this->owner->getCommentsOption('comments_holder_id');
}
/**
* Permission codes required in order to post (or empty if none required)
*
* @return string|array Permission or list of permissions, if required
*/
public function getPostingRequiredPermission()
{
return $this->owner->getCommentsOption('required_permission');
}
/**
* Determine if a user can post comments on this item
*
* @param Member $member Member to check
*
* @return boolean
*/
public function canPostComment($member = null)
{
// Deny if not enabled for this object
if (!$this->owner->CommentsEnabled) {
return false;
}
if (!$this->owner->canView($member)) {
// deny if current user cannot view the underlying record.
return false;
}
// Check if member is required
$requireLogin = $this->owner->CommentsRequireLogin;
if (!$requireLogin) {
return true;
}
// Check member is logged in
$member = $member ?: Security::getCurrentUser();
if (!$member) {
return false;
}
// If member required check permissions
$requiredPermission = $this->owner->PostingRequiredPermission;
if ($requiredPermission && !Permission::checkMember($member, $requiredPermission)) {
return false;
}
return true;
}
/**
* Determine if this member can moderate comments in the CMS
*
* @param Member $member
*
* @return boolean
*/
public function canModerateComments($member = null)
{
// Deny if not enabled for this object
if (!$this->owner->CommentsEnabled) {
return false;
}
// Fallback to can-edit
return $this->owner->canEdit($member);
}
/**
* Gets the RSS link to all comments
*
* @return string
*/
public function getCommentRSSLink()
{
return Director::absoluteURL('comments/rss');
}
/**
* Get the RSS link to all comments on this page
*
* @return string
*/
public function getCommentRSSLinkPage()
{
return Controller::join_links(
$this->getCommentRSSLink(),
str_replace('\\', '-', get_class($this->owner)),
$this->owner->ID
);
}
/**
* Comments interface for the front end. Includes the CommentAddForm and the composition
* of the comments display.
*
* To customize the html see templates/CommentInterface.ss or extend this function with
* your own extension.
*
* @todo Cleanup the passing of all this configuration based functionality
*
* @see docs/en/Extending
*/
public function CommentsForm()
{
// Check if enabled
$enabled = $this->getCommentsEnabled();
if ($enabled && $this->owner->getCommentsOption('include_js')) {
Requirements::javascript('silverstripe/comments:client/dist/js/jquery.min.js');
Requirements::javascript('silverstripe/comments:client/dist/js/jquery-validation/jquery.validate.min.js');
Requirements::javascript('silverstripe/admin:client/dist/js/i18n.js');
Requirements::add_i18n_javascript('silverstripe/comments:client/lang');
Requirements::javascript('silverstripe/comments:client/dist/js/CommentsInterface.js');
}
$controller = CommentingController::create();
$controller->setOwnerRecord($this->owner);
$controller->setParentClass($this->owner->getClassName());
$controller->setOwnerController(Controller::curr());
$session = Controller::curr()->getRequest()->getSession();
$moderatedSubmitted = $session->get('CommentsModerated');
$session->clear('CommentsModerated');
$form = ($enabled) ? $controller->CommentsForm() : false;
// a little bit all over the show but to ensure a slightly easier upgrade for users
// return back the same variables as previously done in comments
return $this
->owner
->customise([
'AddCommentForm' => $form,
'ModeratedSubmitted' => $moderatedSubmitted,
])
->renderWith('CommentsInterface');
}
/**
* Returns whether this extension instance is attached to a {@link SiteTree} object
*
* @return bool
*/
public function attachedToSiteTree()
{
$class = $this->owner->baseClass();
return (is_subclass_of($class, SiteTree::class)) || ($class == SiteTree::class);
}
/**
* Get the commenting option for this object.
*
* This can be overridden in any instance or extension to customise the
* option available.
*
* @param string $key
*
* @return mixed Result if the setting is available, or null otherwise
*/
public function getCommentsOption($key)
{
$settings = $this->getCommentsOptions();
$value = null;
if (isset($settings[$key])) {
$value = $settings[$key];
}
// To allow other extensions to customise this option
if ($this->owner) {
$this->owner->extend('updateCommentsOption', $key, $value);
}
return $value;
}
/**
* @return array
*/
public function getCommentsOptions()
{
if ($this->owner) {
$settings = $this->owner->config()->get('comments');
} else {
$settings = Config::inst()->get(__CLASS__, 'comments');
}
return $settings;
}
/**
* Add moderation functions to the current fieldlist
*
* @param FieldList $fields
*/
protected function updateModerationFields(FieldList $fields)
{
Requirements::css('silverstripe/comments:client/dist/styles/cms.css');
$newComments = $this->owner->AllComments()->filter('Moderated', 0);
$newGrid = CommentsGridField::create(
'NewComments',
_t('CommentsAdmin.NewComments', 'New'),
$newComments,
CommentsGridFieldConfig::create()
);
$approvedComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 0);
$approvedGrid = new CommentsGridField(
'ApprovedComments',
_t('CommentsAdmin.Comments', 'Approved'),
$approvedComments,
CommentsGridFieldConfig::create()
);
$spamComments = $this->owner->AllComments()->filter('Moderated', 1)->filter('IsSpam', 1);
$spamGrid = CommentsGridField::create(
'SpamComments',
_t('CommentsAdmin.SpamComments', 'Spam'),
$spamComments,
CommentsGridFieldConfig::create()
);
$newCount = '(' . count($newComments ?? []) . ')';
$approvedCount = '(' . count($approvedComments ?? []) . ')';
$spamCount = '(' . count($spamComments ?? []) . ')';
if ($fields->hasTabSet()) {
$tabs = TabSet::create(
'Comments',
Tab::create(
'CommentsNewCommentsTab',
_t('SilverStripe\\Comments\\Admin\\CommentAdmin.NewComments', 'New') . ' ' . $newCount,
$newGrid
),
Tab::create(
'CommentsCommentsTab',
_t('SilverStripe\\Comments\\Admin\\CommentAdmin.Comments', 'Approved') . ' ' . $approvedCount,
$approvedGrid
),
Tab::create(
'CommentsSpamCommentsTab',
_t('SilverStripe\\Comments\\Admin\\CommentAdmin.SpamComments', 'Spam') . ' ' . $spamCount,
$spamGrid
)
);
$tabs->setTitle(_t(__CLASS__ . '.COMMENTSTABSET', 'Comments'));
$fields->addFieldToTab('Root', $tabs);
} else {
$fields->push($newGrid);
$fields->push($approvedGrid);
$fields->push($spamGrid);
}
}
public function updateCMSFields(FieldList $fields)
{
// Disable moderation if not permitted
if ($this->owner->canModerateComments()) {
$this->updateModerationFields($fields);
}
// If this isn't a page we should merge the settings into the CMS fields
if (!$this->attachedToSiteTree()) {
$this->updateSettingsFields($fields);
}
}
}

299
src/Forms/CommentForm.php Normal file
View File

@ -0,0 +1,299 @@
<?php
namespace SilverStripe\Comments\Forms;
use SilverStripe\Comments\Controllers\CommentingController;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\EmailField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormAction;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\Security\Security;
class CommentForm extends Form
{
/**
* @param string $name
* @param CommentingController $controller
*/
public function __construct($name, CommentingController $controller)
{
$usePreview = $controller->getOption('use_preview');
$nameRequired = _t('CommentInterface.YOURNAME_MESSAGE_REQUIRED', 'Please enter your name');
$emailRequired = _t('CommentInterface.EMAILADDRESS_MESSAGE_REQUIRED', 'Please enter your email address');
$emailInvalid = _t('CommentInterface.EMAILADDRESS_MESSAGE_EMAIL', 'Please enter a valid email address');
$urlInvalid = _t('CommentInterface.COMMENT_MESSAGE_URL', 'Please enter a valid URL');
$commentRequired = _t('CommentInterface.COMMENT_MESSAGE_REQUIRED', 'Please enter your comment');
$fields = FieldList::create(
$dataFields = CompositeField::create(
// Name
$a = TextField::create('Name', _t('CommentInterface.YOURNAME', 'Your name'))
->setCustomValidationMessage($nameRequired)
->setAttribute('data-msg-required', $nameRequired),
// Email
EmailField::create(
'Email',
_t(
'SilverStripe\\Comments\\Controllers\\CommentingController.EMAILADDRESS',
'Your email address (will not be published)'
)
)
->setCustomValidationMessage($emailRequired)
->setAttribute('data-msg-required', $emailRequired)
->setAttribute('data-msg-email', $emailInvalid)
->setAttribute('data-rule-email', true),
// Url
TextField::create('URL', _t(
'SilverStripe\\Comments\\Controllers\\CommentingController.WEBSITEURL',
'Your website URL'
))
->setAttribute('data-msg-url', $urlInvalid)
->setAttribute('data-rule-url', true),
// Comment
TextareaField::create('Comment', _t(
'SilverStripe\\Comments\\Controllers\\CommentingController.COMMENTS',
'Comments'
))
->setCustomValidationMessage($commentRequired)
->setAttribute('data-msg-required', $commentRequired)
),
HiddenField::create('ParentID'),
HiddenField::create('ParentClassName'),
HiddenField::create('ReturnURL'),
HiddenField::create('ParentCommentID')
);
// Preview formatted comment. Makes most sense when shortcodes or
// limited HTML is allowed. Populated by JS/Ajax.
if ($usePreview) {
$fields->insertAfter(
'Comment',
ReadonlyField::create('PreviewComment', _t('CommentInterface.PREVIEWLABEL', 'Preview'))
->setAttribute('style', 'display: none') // enable through JS
);
}
$dataFields->addExtraClass('data-fields');
// save actions
$actions = FieldList::create(
$postAction = new FormAction('doPostComment', _t('CommentInterface.POST', 'Post'))
);
if ($usePreview) {
$actions->push(
FormAction::create('doPreviewComment', _t('CommentInterface.PREVIEW', 'Preview'))
->addExtraClass('action-minor')
->setAttribute('style', 'display: none') // enable through JS
);
}
$required = RequiredFields::create(
$controller->config()->required_fields
);
parent::__construct($controller, $name, $fields, $actions, $required);
// if the record exists load the extra required data
if ($record = $controller->getOwnerRecord()) {
// Load member data
$member = Security::getCurrentUser();
if (($record->CommentsRequireLogin || $record->PostingRequiredPermission) && $member) {
$fields = $this->Fields();
$fields->removeByName('Name');
$fields->removeByName('Email');
$fields->insertBefore(
ReadonlyField::create(
'NameView',
_t('CommentInterface.YOURNAME', 'Your name'),
$member->getName()
),
'URL'
);
$fields->push(HiddenField::create('Name', '', $member->getName()));
$fields->push(HiddenField::create('Email', '', $member->Email));
}
// we do not want to read a new URL when the form has already been submitted
// which in here, it hasn't been.
$this->loadDataFrom([
'ParentID' => $record->ID,
'ReturnURL' => $controller->getRequest()->getURL(),
'ParentClassName' => $controller->getParentClass()
]);
}
// Set it so the user gets redirected back down to the form upon form fail
$this->setRedirectToFormOnValidationError(true);
// load any data from the session
$data = $this->getSessionData();
if (!is_array($data)) {
return;
}
// load user data from previous form request back into form.
if (array_key_exists('UserData', $data ?? [])) {
$formData = json_decode($data['UserData'] ?? '', true);
$this->loadDataFrom([
'Name' => isset($formData['Name']) ? $formData['Name'] : '',
'URL' => isset($formData['URL']) ? $formData['URL'] : '',
'Email' => isset($formData['Email']) ? $formData['Email'] : ''
]);
}
// allow previous value to fill if comment
if (array_key_exists('Comment', $data ?? [])) {
$prevComment = $data['Comment'];
if ($prevComment && $prevComment != '') {
$this->loadDataFrom(['Comment' => $prevComment]);
}
}
}
/**
* @param array $data
* @param Form $form
* @return HTTPResponse
*/
public function doPreviewComment($data, $form)
{
$data['IsPreview'] = 1;
return $this->doPostComment($data, $form);
}
/**
* Process which creates a {@link Comment} once a user submits a comment from this form.
*
* @param array $data
* @param Form $form
* @return HTTPResponse
*/
public function doPostComment($data, $form)
{
// Load class and parent from data
if (isset($data['ParentClassName'])) {
$this->controller->setParentClass($data['ParentClassName']);
}
if (isset($data['ParentID']) && ($class = $this->controller->getParentClass())) {
$this->controller->setOwnerRecord($class::get()->byID($data['ParentID']));
}
if (!$this->controller->getOwnerRecord()) {
return $this->getRequestHandler()->httpError(404);
}
// cache users data
$form->setSessionData([
'UserData' => json_encode($data),
'Comment' => $data['Comment']
]);
// extend hook to allow extensions. Also see onAfterPostComment
$this->controller->extend('onBeforePostComment', $form);
// If commenting can only be done by logged in users, make sure the user is logged in
if (!$this->controller->getOwnerRecord()->canPostComment()) {
return Security::permissionFailure(
$this->controller,
_t(
'SilverStripe\\Comments\\Controllers\\CommentingController.PERMISSIONFAILURE',
"You're not able to post comments to this page. Please ensure you are logged in and have an "
. 'appropriate permission level.'
)
);
}
if ($member = Security::getCurrentUser()) {
$form->Fields()->push(HiddenField::create('AuthorID', 'Author ID', $member->ID));
}
// What kind of moderation is required?
switch ($this->controller->getOwnerRecord()->ModerationRequired) {
case 'Required':
$requireModeration = true;
break;
case 'NonMembersOnly':
$requireModeration = empty($member);
break;
case 'None':
default:
$requireModeration = false;
break;
}
$comment = Comment::create();
$form->saveInto($comment);
$comment->ParentID = $data['ParentID'];
$comment->ParentClass = $data['ParentClassName'];
$comment->AllowHtml = $this->controller->getOption('html_allowed');
$comment->Moderated = !$requireModeration;
// Save into DB, or call pre-save hooks to give accurate preview
$usePreview = $this->controller->getOption('use_preview');
$isPreview = $usePreview && !empty($data['IsPreview']);
if ($isPreview) {
$comment->extend('onBeforeWrite');
} else {
$comment->write();
// extend hook to allow extensions. Also see onBeforePostComment
$this->controller->extend('onAfterPostComment', $comment);
}
// we want to show a notification if comments are moderated
if ($requireModeration && !$comment->IsSpam) {
$this->getRequest()->getSession()->set('CommentsModerated', 1);
}
// clear the users comment since the comment was successful.
if ($comment->exists()) {
// Remove the comment data as it's been saved already.
unset($data['Comment']);
}
// cache users data (name, email, etc to prepopulate on other forms).
$form->setSessionData([
'UserData' => json_encode($data),
]);
// Find parent link
if (!empty($data['ReturnURL'])) {
$url = $data['ReturnURL'];
} elseif ($parent = $comment->Parent()) {
$url = $parent->Link();
} else {
return $this->controller->redirectBack();
}
// Given a redirect page exists, attempt to link to the correct anchor
if ($comment->IsSpam) {
// Link to the form with the error message contained
$hash = $form->FormName();
} elseif (!$comment->Moderated) {
// Display the "awaiting moderation" text
$hash = 'moderated';
} else {
// Link to the moderated, non-spam comment
$hash = $comment->Permalink();
}
return $this->controller->redirect(Controller::join_links($url, "#{$hash}"));
}
}

926
src/Model/Comment.php Executable file
View File

@ -0,0 +1,926 @@
<?php
namespace SilverStripe\Comments\Model;
use SilverStripe\Dev\Deprecation;
use HTMLPurifier;
use HTMLPurifier_Config;
use SilverStripe\Comments\Controllers\CommentingController;
use SilverStripe\Comments\Extensions\CommentsExtension;
use SilverStripe\Comments\Model\Comment\SecurityToken;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\TempFolder;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\EmailField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\HeaderField;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\ORM\SS_List;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
/**
* Represents a single comment object.
*
* @property string $Name
* @property string $Comment
* @property string $Email
* @property string $URL
* @property string $BaseClass
* @property boolean $Moderated
* @property boolean $IsSpam True if the comment is known as spam
* @property integer $ParentID ID of the parent page / dataobject
* @property boolean $AllowHtml If true, treat $Comment as HTML instead of plain text
* @property string $SecretToken Secret admin token required to provide moderation links between sessions
* @property integer $Depth Depth of this comment in the nested chain
*
* @method HasManyList ChildComments() List of child comments
* @method Member Author() Member object who created this comment
* @method Comment ParentComment() Parent comment this is a reply to
* @package comments
*/
class Comment extends DataObject
{
/**
* {@inheritDoc}
*/
private static $db = array(
'Name' => 'Varchar(200)',
'Comment' => 'Text',
'Email' => 'Varchar(200)',
'URL' => 'Varchar(255)',
'Moderated' => 'Boolean(0)',
'IsSpam' => 'Boolean(0)',
'AllowHtml' => 'Boolean',
'SecretToken' => 'Varchar(255)',
'Depth' => 'Int'
);
/**
* {@inheritDoc}
*/
private static $has_one = array(
'Author' => Member::class,
'ParentComment' => self::class,
'Parent' => DataObject::class
);
/**
* {@inheritDoc}
*/
private static $has_many = array(
'ChildComments' => self::class
);
/**
* {@inheritDoc}
*/
private static $default_sort = '"Created" DESC';
/**
* {@inheritDoc}
*/
private static $defaults = array(
'Moderated' => 0,
'IsSpam' => 0,
);
/**
* {@inheritDoc}
*/
private static $casting = array(
'Title' => 'Varchar',
'ParentTitle' => 'Varchar',
'ParentClassName' => 'Varchar',
'AuthorName' => 'Varchar',
'RSSName' => 'Varchar',
'DeleteLink' => 'Varchar',
'Date' => 'Datetime',
'SpamLink' => 'Varchar',
'HamLink' => 'Varchar',
'ApproveLink' => 'Varchar',
'Permalink' => 'Varchar'
);
/**
* {@inheritDoc}
*/
private static $searchable_fields = array(
'Name',
'Email',
'Comment',
'Created'
);
/**
* {@inheritDoc}
*/
private static $summary_fields = array(
'getAuthorName' => 'Submitted By',
'getAuthorEmail' => 'Email',
'Comment.LimitWordCount' => 'Comment',
'Created' => 'Date Posted',
'Parent.Title' => 'Post',
'IsSpam' => 'Is Spam'
);
/**
* {@inheritDoc}
*/
private static $field_labels = array(
'Author' => 'Author Member'
);
/**
* {@inheritDoc}
*/
private static $table_name = 'Comment';
/**
* {@inheritDoc}
*/
public function onBeforeWrite()
{
parent::onBeforeWrite();
// Sanitize HTML, because its expected to be passed to the template unescaped later
if ($this->AllowHtml) {
$this->Comment = $this->purifyHtml($this->Comment);
}
// Check comment depth
$this->updateDepth();
}
/**
* {@inheritDoc}
*/
public function onBeforeDelete()
{
parent::onBeforeDelete();
// Delete all children
foreach ($this->ChildComments() as $comment) {
$comment->delete();
}
}
/**
* @return Comment_SecurityToken
*/
public function getSecurityToken()
{
return Injector::inst()->createWithArgs(SecurityToken::class, array($this));
}
/**
* Return a link to this comment
*
* @param string $action
*
* @return string link to this comment.
*/
public function Link($action = '')
{
if ($parent = $this->Parent()) {
return $parent->Link($action) . '#' . $this->Permalink();
}
}
/**
* Returns the permalink for this {@link Comment}. Inserted into
* the ID tag of the comment
*
* @return string
*/
public function Permalink()
{
$prefix = $this->getOption('comment_permalink_prefix');
return $prefix . $this->ID;
}
/**
* Translate the form field labels for the CMS administration
*
* @param boolean $includerelations
*
* @return array
*/
public function fieldLabels($includerelations = true)
{
$labels = parent::fieldLabels($includerelations);
$labels['Name'] = _t(__CLASS__ . '.NAME', 'Author name');
$labels['Comment'] = _t(__CLASS__ . '.COMMENT', 'Comment');
$labels['Email'] = _t(__CLASS__ . '.EMAIL', 'Email');
$labels['URL'] = _t(__CLASS__ . '.URL', 'URL');
$labels['IsSpam'] = _t(__CLASS__ . '.ISSPAM', 'Spam?');
$labels['Moderated'] = _t(__CLASS__ . '.MODERATED', 'Moderated?');
$labels['ParentTitle'] = _t(__CLASS__ . '.PARENTTITLE', 'Parent');
$labels['Created'] = _t(__CLASS__ . '.CREATED', 'Date posted');
return $labels;
}
/**
* Get the commenting option
*
* @param string $key
*
* @return mixed Result if the setting is available, or null otherwise
*/
public function getOption($key)
{
// If possible use the current record
$record = $this->Parent();
if (!$record && $this->Parent()) {
// Otherwise a singleton of that record
$record = singleton($this->Parent()->dataClass());
} elseif (!$record) {
// Otherwise just use the default options
$record = singleton(CommentsExtension::class);
}
return ($record instanceof CommentsExtension || $record->hasExtension(CommentsExtension::class))
? $record->getCommentsOption($key)
: null;
}
/**
* Returns the parent {@link DataObject} this comment is attached too
*
* @deprecated 4.0.0 Use $this->Parent() instead
* @return DataObject
*/
public function getParent()
{
// this is wrapped in withNoReplacement() because it's called by ViewableData::__get()
// which looks for a `"get$property"` method, which itself is called by
// AssetControllExtension::findAssets()
Deprecation::withNoReplacement(function () {
Deprecation::notice('4.0.0', 'Use $this->Parent() instead');
});
return $this->BaseClass && $this->ParentID
? DataObject::get_by_id($this->BaseClass, $this->ParentID, true)
: null;
}
/**
* Returns a string to help identify the parent of the comment
*
* @return string
*/
public function getParentTitle()
{
if ($parent = $this->Parent()) {
return $parent->Title ?: ($parent->ClassName . ' #' . $parent->ID);
}
}
/**
* Comment-parent classnames obviously vary, return the parent classname
*
* @return string
*/
public function getParentClassName()
{
return $this->Parent()->getClassName();
}
/**
* {@inheritDoc}
*/
public function castingHelper($field)
{
// Safely escape the comment
if (in_array($field, ['EscapedComment', 'Comment'], true)) {
return $this->AllowHtml ? 'HTMLText' : 'Text';
}
return parent::castingHelper($field);
}
/**
* Content to be safely escaped on the frontend
*
* @return string
*/
public function getEscapedComment()
{
return $this->Comment;
}
/**
* Return whether this comment is a preview (has not been written to the db)
*
* @return boolean
*/
public function isPreview()
{
return !$this->exists();
}
/**
* @todo needs to compare to the new {@link Commenting} configuration API
*
* @param Member $member
* @param array $context
* @return bool
*/
public function canCreate($member = null, $context = [])
{
return false;
}
/**
* Checks for association with a page, and {@link SiteTree->ProvidePermission}
* flag being set to true.
*
* @param Member $member
* @return Boolean
*/
public function canView($member = null)
{
$member = $this->getMember($member);
$extended = $this->extendedCan('canView', $member);
if ($extended !== null) {
return $extended;
}
if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
return true;
}
if ($parent = $this->Parent()) {
return $parent->canView($member)
&& $parent->hasExtension(CommentsExtension::class)
&& $parent->CommentsEnabled;
}
return false;
}
/**
* Checks if the comment can be edited.
*
* @param null|int|Member $member
* @return Boolean
*/
public function canEdit($member = null)
{
$member = $this->getMember($member);
if (!$member) {
return false;
}
$extended = $this->extendedCan('canEdit', $member);
if ($extended !== null) {
return $extended;
}
if (Permission::checkMember($member, 'CMS_ACCESS_CommentAdmin')) {
return true;
}
if ($parent = $this->Parent()) {
return $parent->canEdit($member);
}
return false;
}
/**
* Checks if the comment can be deleted.
*
* @param null|int|Member $member
* @return Boolean
*/
public function canDelete($member = null)
{
$member = $this->getMember($member);
if (!$member) {
return false;
}
$extended = $this->extendedCan('canDelete', $member);
if ($extended !== null) {
return $extended;
}
return $this->canEdit($member);
}
/**
* Resolves Member object.
*
* @param Member|int|null $member
* @return Member|null
*/
protected function getMember($member = null)
{
if (!$member) {
$member = Security::getCurrentUser();
}
if (is_numeric($member)) {
$member = DataObject::get_by_id(Member::class, $member, true);
}
return $member;
}
/**
* Return the authors name for the comment
*
* @return string
*/
public function getAuthorName()
{
if ($this->Name) {
return $this->Name;
} elseif ($author = $this->Author()) {
return $author->getName();
}
}
/**
* Return the comment authors email address
*
* @return string
*/
public function getAuthorEmail()
{
if ($this->Email) {
return $this->Email;
} elseif ($author = $this->Author()) {
return $author->Email;
}
}
/**
* Generate a secure admin-action link authorised for the specified member
*
* @param string $action An action on CommentingController to link to
* @param Member $member The member authorised to invoke this action
*
* @return string
*/
protected function actionLink($action, $member = null)
{
if (!$member) {
$member = Security::getCurrentUser();
}
if (!$member) {
return false;
}
/**
* @todo: How do we handle "DataObject" instances that don't have a Link to reject/spam/delete?? This may
* we have to make CMS a hard dependency instead.
*/
// if (!$this->Parent()->hasMethod('Link')) {
// return false;
// }
$url = Controller::join_links(
Director::baseURL(),
'comments',
$action,
$this->ID
);
// Limit access for this user
$token = $this->getSecurityToken();
return $token->addToUrl($url, $member);
}
/**
* Link to delete this comment
*
* @param Member $member
*
* @return string
*/
public function DeleteLink($member = null)
{
if ($this->canDelete($member)) {
return $this->actionLink('delete', $member);
}
}
/**
* Link to mark as spam
*
* @param Member $member
*
* @return string
*/
public function SpamLink($member = null)
{
if ($this->canEdit($member) && !$this->IsSpam) {
return $this->actionLink('spam', $member);
}
}
/**
* Link to mark as not-spam (ham)
*
* @param Member $member
*
* @return string
*/
public function HamLink($member = null)
{
if ($this->canEdit($member) && $this->IsSpam) {
return $this->actionLink('ham', $member);
}
}
/**
* Link to approve this comment
*
* @param Member $member
*
* @return string
*/
public function ApproveLink($member = null)
{
if ($this->canEdit($member) && !$this->Moderated) {
return $this->actionLink('approve', $member);
}
}
/**
* Mark this comment as spam
*/
public function markSpam()
{
$this->IsSpam = true;
$this->Moderated = true;
$this->write();
$this->extend('afterMarkSpam');
}
/**
* Mark this comment as approved
*/
public function markApproved()
{
$this->IsSpam = false;
$this->Moderated = true;
$this->write();
$this->extend('afterMarkApproved');
}
/**
* Mark this comment as unapproved
*/
public function markUnapproved()
{
$this->Moderated = false;
$this->write();
$this->extend('afterMarkUnapproved');
}
/**
* @return string
*/
public function SpamClass()
{
if ($this->IsSpam) {
return 'spam';
} elseif (!$this->Moderated) {
return 'unmoderated';
} else {
return 'notspam';
}
}
/**
* @return string
*/
public function getTitle()
{
$title = sprintf(_t(__CLASS__ . '.COMMENTBY', 'Comment by %s', 'Name') ?? '', $this->getAuthorName());
if ($parent = $this->Parent()) {
if ($parent->Title) {
$title .= sprintf(' %s %s', _t(__CLASS__ . '.ON', 'on'), $parent->Title);
}
}
return $title;
}
/*
* Modify the default fields shown to the user
*/
public function getCMSFields()
{
$commentField = $this->AllowHtml ? HTMLEditorField::class : TextareaField::class;
$fields = new FieldList(
$this
->obj('Created')
->scaffoldFormField($this->fieldLabel('Created'))
->performReadonlyTransformation(),
TextField::create('Name', $this->fieldLabel('Name')),
$commentField::create('Comment', $this->fieldLabel('Comment')),
EmailField::create('Email', $this->fieldLabel('Email')),
TextField::create('URL', $this->fieldLabel('URL')),
FieldGroup::create(array(
CheckboxField::create('Moderated', $this->fieldLabel('Moderated')),
CheckboxField::create('IsSpam', $this->fieldLabel('IsSpam')),
))
->setTitle(_t(__CLASS__ . '.OPTIONS', 'Options'))
->setDescription(_t(
__CLASS__ . '.OPTION_DESCRIPTION',
'Unmoderated and spam comments will not be displayed until approved'
))
);
// Show member name if given
if (($author = $this->Author()) && $author->exists()) {
$fields->insertAfter(
'Name',
TextField::create('AuthorMember', $this->fieldLabel('Author'), $author->Title)
->performReadonlyTransformation()
);
}
// Show parent comment if given
if (($parent = $this->ParentComment()) && $parent->exists()) {
$fields->push(new HeaderField(
'ParentComment_Title',
_t(__CLASS__ . '.ParentComment_Title', 'This comment is a reply to the below')
));
// Created date
// FIXME - the method setName in DatetimeField is not chainable, hence
// the lack of chaining here
$createdField = $parent
->obj('Created')
->scaffoldFormField($parent->fieldLabel('Created'));
$createdField->setName('ParentComment_Created');
$createdField->setValue($parent->Created);
$createdField->performReadonlyTransformation();
$fields->push($createdField);
// Name (could be member or string value)
$fields->push(
$parent
->obj('AuthorName')
->scaffoldFormField($parent->fieldLabel('AuthorName'))
->setName('ParentComment_AuthorName')
->setValue($parent->getAuthorName())
->performReadonlyTransformation()
);
// Comment body
$fields->push(
$parent
->obj('EscapedComment')
->scaffoldFormField($parent->fieldLabel(self::class))
->setName('ParentComment_EscapedComment')
->setValue($parent->Comment)
->performReadonlyTransformation()
);
}
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* @param string $dirtyHtml
*
* @return string
*/
public function purifyHtml($dirtyHtml)
{
if ($service = $this->getHtmlPurifierService()) {
return $service->purify($dirtyHtml);
}
return $dirtyHtml;
}
/**
* @return HTMLPurifier (or anything with a "purify()" method)
*/
public function getHtmlPurifierService()
{
if (!class_exists(HTMLPurifier_Config::class)) {
return null;
}
$config = HTMLPurifier_Config::createDefault();
$allowedElements = (array) $this->getOption('html_allowed_elements');
if (!empty($allowedElements)) {
$config->set('HTML.AllowedElements', $allowedElements);
}
// This injector cannot be set unless the 'p' element is allowed
if (in_array('p', $allowedElements ?? [])) {
$config->set('AutoFormat.AutoParagraph', true);
}
$config->set('AutoFormat.Linkify', true);
$config->set('URI.DisableExternalResources', true);
$config->set('Cache.SerializerPath', TempFolder::getTempFolder(BASE_PATH));
return new HTMLPurifier($config);
}
/**
* Calculate the Gravatar link from the email address
*
* @return string
*/
public function Gravatar()
{
$gravatar = '';
$use_gravatar = $this->getOption('use_gravatar');
if ($use_gravatar) {
$gravatar = 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->Email ?? '')));
$gravatarsize = $this->getOption('gravatar_size');
$gravatardefault = $this->getOption('gravatar_default');
$gravatarrating = $this->getOption('gravatar_rating');
$gravatar .= '?' . http_build_query(array(
's' => $gravatarsize,
'd' => $gravatardefault,
'r' => $gravatarrating,
));
}
return $gravatar;
}
/**
* Determine if replies are enabled for this instance
*
* @return boolean
*/
public function getRepliesEnabled()
{
// Check reply option
if (!$this->getOption('nested_comments')) {
return false;
}
// Check if depth is limited
$maxLevel = $this->getOption('nested_depth');
$notSpam = ($this->SpamClass() == 'notspam');
return $notSpam && (!$maxLevel || $this->Depth < $maxLevel);
}
/**
* Proxy for checking whether the has permission to comment on the comment parent.
*
* @param Member $member Member to check
*
* @return boolean
*/
public function canPostComment($member = null)
{
return $this->Parent()
&& $this->Parent()->exists()
&& $this->Parent()->canPostComment($member);
}
/**
* Returns the list of all replies
*
* @return SS_List
*/
public function AllReplies()
{
// No replies if disabled
if (!$this->getRepliesEnabled()) {
return new ArrayList();
}
// Get all non-spam comments
$order = $this->getOption('order_replies_by')
?: $this->getOption('order_comments_by');
$list = $this
->ChildComments()
->sort($order);
$this->extend('updateAllReplies', $list);
return $list;
}
/**
* Returns the list of replies, with spam and unmoderated items excluded, for use in the frontend
*
* @return SS_List
*/
public function Replies()
{
// No replies if disabled
if (!$this->getRepliesEnabled()) {
return new ArrayList();
}
$list = $this->AllReplies();
// Filter spam comments for non-administrators if configured
$parent = $this->Parent();
$showSpam = $this->getOption('frontend_spam') && $parent && $parent->canModerateComments();
if (!$showSpam) {
$list = $list->filter('IsSpam', 0);
}
// Filter un-moderated comments for non-administrators if moderation is enabled
$showUnmoderated = $parent && (
($parent->ModerationRequired === 'None')
|| ($this->getOption('frontend_moderation') && $parent->canModerateComments())
);
if (!$showUnmoderated) {
$list = $list->filter('Moderated', 1);
}
$this->extend('updateReplies', $list);
return $list;
}
/**
* Returns the list of replies paged, with spam and unmoderated items excluded, for use in the frontend
*
* @return PaginatedList
*/
public function PagedReplies()
{
$list = $this->Replies();
// Add pagination
$list = new PaginatedList($list, Controller::curr()->getRequest());
$list->setPaginationGetVar('repliesstart' . $this->ID);
$list->setPageLength($this->getOption('comments_per_page'));
$this->extend('updatePagedReplies', $list);
return $list;
}
/**
* Generate a reply form for this comment
*
* @return Form
*/
public function ReplyForm()
{
// Ensure replies are enabled
if (!$this->getRepliesEnabled()) {
return null;
}
// Check parent is available
$parent = $this->Parent();
if (!$parent || !$parent->exists()) {
return null;
}
// Build reply controller
$controller = CommentingController::create();
$controller->setOwnerRecord($parent);
$controller->setParentClass($parent->ClassName);
$controller->setOwnerController(Controller::curr());
return $controller->ReplyForm($this);
}
/**
* @return string
*/
public function getDate()
{
return $this->Created;
}
/**
* Refresh of this comment in the hierarchy
*/
public function updateDepth()
{
$parent = $this->ParentComment();
if ($parent && $parent->exists()) {
$parent->updateDepth();
$this->Depth = $parent->Depth + 1;
} else {
$this->Depth = 1;
}
}
}

View File

@ -0,0 +1,121 @@
<?php
namespace SilverStripe\Comments\Model\Comment;
use SilverStripe\Control\Controller;
use SilverStripe\Security\Member;
use SilverStripe\Security\RandomGenerator;
use SilverStripe\Security\Security;
/**
* Provides the ability to generate cryptographically secure tokens for comment moderation
*/
class SecurityToken
{
/**
* @var string
*/
private $secret = null;
/**
* @param Comment $comment Comment to generate this token for
*/
public function __construct($comment)
{
if (!$comment->SecretToken) {
$comment->SecretToken = $this->generate();
$comment->write();
}
$this->secret = $comment->SecretToken;
}
/**
* Generate the token for the given salt and current secret
*
* @param string $salt
*
* @return string
*/
protected function getToken($salt)
{
return hash_pbkdf2('sha256', $this->secret ?? '', $salt ?? '', 1000, 30);
}
/**
* Get the member-specific salt.
*
* The reason for making the salt specific to a user is that it cannot be "passed in" via a
* querystring, requiring the same user to be present at both the link generation and the
* controller action.
*
* @param string $salt Single use salt
* @param Member $member Member object
*
* @return string Generated salt specific to this member
*/
protected function memberSalt($salt, $member)
{
// Fallback to salting with ID in case the member has not one set
return $salt . ($member->Salt ?: $member->ID);
}
/**
* @param string $url Comment action URL
* @param Member $member Member to restrict access to this action to
*
* @return string
*/
public function addToUrl($url, $member)
{
$salt = $this->generate(15); // New random salt; Will be passed into url
// Generate salt specific to this member
$memberSalt = $this->memberSalt($salt, $member);
$token = $this->getToken($memberSalt);
return Controller::join_links(
$url,
sprintf(
'?t=%s&s=%s',
urlencode($token ?? ''),
urlencode($salt ?? '')
)
);
}
/**
* @param SS_HTTPRequest $request
*
* @return boolean
*/
public function checkRequest($request)
{
$member = Security::getCurrentUser();
if (!$member) {
return false;
}
$salt = $request->getVar('s');
$memberSalt = $this->memberSalt($salt, $member);
$token = $this->getToken($memberSalt);
// Ensure tokens match
return $token === $request->getVar('t');
}
/**
* Generates new random key
*
* @param integer $length
*
* @return string
*/
protected function generate($length = null)
{
$generator = new RandomGenerator();
$result = $generator->randomToken('sha256');
if ($length !== null) {
return substr($result ?? '', 0, $length);
}
return $result;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace SilverStripe\Comments\Tasks;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\BuildTask;
use SilverStripe\ORM\DB;
/**
* Migrates all 3.x comment's BaseClass fields to the new ParentClass fields
*
* @package comments
*/
class MigrateCommentParentsTask extends BuildTask
{
private static $segment = 'MigrateCommentParentsTask';
protected $title = 'Migrate Comment Parent classes from 3.x';
protected $description = 'Migrates all 3.x Comment BaseClass fields to the new ParentClass fields in 4.0';
/**
* @param HTTPRequest $request
*/
public function run($request)
{
// Check if anything needs to be done
$tableName = Comment::getSchema()->tableName(Comment::class);
if (!DB::get_schema()->hasField($tableName, 'BaseClass')) {
DB::alteration_message('"BaseClass" does not exist on "' . $tableName . '", nothing to upgrade.', 'notice');
return;
}
// Set the class names to fully qualified class names first
$remapping = Config::inst()->get('SilverStripe\\ORM\\DatabaseAdmin', 'classname_value_remapping');
$updateQuery = "UPDATE \"Comment\" SET \"BaseClass\" = ? WHERE \"BaseClass\" = ?";
foreach ($remapping as $old => $new) {
DB::prepared_query($updateQuery, [$new, $old]);
}
// Move these values to ParentClass (the 4.x column name)
DB::query('UPDATE "Comment" SET "ParentClass" = "BaseClass"');
DB::alteration_message('Finished updating any applicable Comment class columns', 'notice');
}
}

View File

@ -1,73 +1,53 @@
<% if CommentsEnabled %>
<% require themedCSS('client/dist/styles/comments', 'comments') %>
<% if $CommentsEnabled %>
<div id="$CommentHolderID" class="comments-holder-container">
<h4><% _t('CommentsInterface_ss.POSTCOM','Post your comment') %></h4>
<% if AddCommentForm %>
<% if CanPost %>
<% if ModeratedSubmitted %>
<p id="$CommentHolderID_PostCommentForm_error" class="message good"><% _t('CommentsInterface_ss.AWAITINGMODERATION', 'Your comment has been submitted and is now awaiting moderation.') %></p>
<% if $AddCommentForm %>
<% if $canPostComment %>
<% if $ModeratedSubmitted %>
<p id="moderated" class="message good" tabindex="-1"><% _t('CommentsInterface_ss.AWAITINGMODERATION', 'Your comment has been submitted and is now awaiting moderation.') %></p>
<% end_if %>
$AddCommentForm
<% else %>
<p><% _t('CommentsInterface_ss.COMMENTLOGINERROR', 'You cannot post comments until you have logged in') %><% if PostingRequiresPermission %>,<% _t('CommentsInterface_ss.COMMENTPERMISSIONERROR', 'and that you have an appropriate permission level') %><% end_if %>.
<p><% _t('CommentsInterface_ss.COMMENTLOGINERROR', 'You cannot post comments until you have logged in') %><% if $PostingRequiredPermission %>, <% _t('CommentsInterface_ss.COMMENTPERMISSIONERROR', 'and have an appropriate permission level') %><% end_if %>.
<a href="Security/login?BackURL={$Parent.Link}" title="<% _t('CommentsInterface_ss.LOGINTOPOSTCOMMENT', 'Login to post a comment') %>"><% _t('CommentsInterface_ss.COMMENTPOSTLOGIN', 'Login Here') %></a>.
</p>
<% end_if %>
<% else %>
<p><% _t('CommentsInterface_ss.COMMENTSDISABLED', 'Posting comments has been disabled') %>.</p>
<p><% _t('CommentsInterface_ss.COMMENTSDISABLED', 'Posting comments has been disabled') %>.</p>
<% end_if %>
<h4><% _t('CommentsInterface_ss.COMMENTS','Comments') %></h4>
<div class="comments-holder">
<% if Comments %>
<ul class="comments-list">
<% loop Comments %>
<li class="comment $EvenOdd<% if FirstLast %> $FirstLast <% end_if %> $SpamClass">
<% if $PagedComments %>
<ul class="comments-list root-level">
<% loop $PagedComments %>
<li class="comment $EvenOdd<% if $FirstLast %> $FirstLast <% end_if %> $SpamClass">
<% include CommentsInterface_singlecomment %>
</li>
<% end_loop %>
</ul>
<% if Comments.MoreThanOnePage %>
<div class="comments-pagination">
<p>
<% if Comments.PrevLink %>
<a href="$Comments.PrevLink" class="previous">&laquo; <% _t('CommentsInterface_ss.PREV','previous') %></a>
<% end_if %>
<% if Comments.Pages %>
<% loop Comments.Pages %>
<% if CurrentBool %>
<strong>$PageNum</strong>
<% else %>
<a href="$Link">$PageNum</a>
<% end_if %>
<% end_loop %>
<% end_if %>
<% if Comments.NextLink %>
<a href="$Comments.NextLink" class="next"><% _t('CommentsInterface_ss.NEXT','next') %> &raquo;</a>
<% end_if %>
</p>
</div>
<% end_if %>
<% with $PagedComments %>
<% include CommentPagination %>
<% end_with %>
<% end_if %>
<p class="no-comments-yet"<% if $Comments.Count %> style='display: none' <% end_if %> ><% _t('CommentsInterface_ss.NOCOMMENTSYET','No one has commented on this page yet.') %></p>
<p class="no-comments-yet"<% if $PagedComments.Count %> style='display: none' <% end_if %> ><% _t('CommentsInterface_ss.NOCOMMENTSYET','No one has commented on this page yet.') %></p>
</div>
<% if DeleteAllLink %>
<% if $DeleteAllLink %>
<p class="delete-comments">
<a href="$DeleteAllLink"><% _t('CommentsInterface_ss.PageCommentInterface.DELETEALLCOMMENTS','Delete all comments on this page') %></a>
<a href="$DeleteAllLink"><% _t('CommentsInterface_ss.DELETEALLCOMMENTS','Delete all comments on this page') %></a>
</p>
<% end_if %>
<p class="commenting-rss-feed">
<a href="$RssLinkPage"><% _t('CommentsInterface_ss.RSSFEEDCOMMENTS', 'RSS feed for comments on this page') %></a> |
<a href="$RssLink"><% _t('CommentsInterface_ss.RSSFEEDALLCOMMENTS', 'RSS feed for all comments') %></a>
<a href="$CommentRSSLinkPage"><% _t('CommentsInterface_ss.RSSFEEDCOMMENTS', 'RSS feed for comments on this page') %></a> |
<a href="$CommentRSSLink"><% _t('CommentsInterface_ss.RSSFEEDALLCOMMENTS', 'RSS feed for all comments') %></a>
</p>
</div>
<% end_if %>

View File

@ -1,31 +0,0 @@
<div class="comment" id="<% if isPreview %>comment-preview<% else %>$Permalink<% end_if %>">
<% if $Gravatar %><img class="gravatar" src="$Gravatar" alt="Gravatar for $Name" title="Gravatar for $Name" /><% end_if %>
$EscapedComment
</div>
<% if not isPreview %>
<p class="info">
<% if $URL %>
<% _t('CommentsInterface_singlecomment_ss.PBY','Posted by') %> <a href="$URL.URL" rel="nofollow">$AuthorName.XML</a>, $Created.Nice ($Created.Ago)
<% else %>
<% _t('CommentsInterface_singlecomment_ss.PBY','Posted by') %> $AuthorName.XML, $Created.Nice ($Created.Ago)
<% end_if %>
</p>
<% if $ApproveLink || $SpamLink || $HamLink || $DeleteLink %>
<ul class="action-links">
<% if ApproveLink %>
<li><a href="$ApproveLink.ATT" class="approve"><% _t('CommentsInterface_singlecomment_ss.APPROVE', 'approve this comment') %></a></li>
<% end_if %>
<% if SpamLink %>
<li><a href="$SpamLink.ATT" class="spam"><% _t('CommentsInterface_singlecomment_ss.ISSPAM','this comment is spam') %></a></li>
<% end_if %>
<% if HamLink %>
<li><a href="$HamLink.ATT" class="ham"><% _t('CommentsInterface_singlecomment_ss.ISNTSPAM','this comment is not spam') %></a></li>
<% end_if %>
<% if DeleteLink %>
<li class="last"><a href="$DeleteLink.ATT" class="delete"><% _t('CommentsInterface_singlecomment_ss.REMCOM','remove this comment') %></a></li>
<% end_if %>
</ul>
<% end_if %>
<% end_if %>

View File

@ -0,0 +1,21 @@
<% if $MoreThanOnePage %>
<div class="comments-pagination">
<p>
<% if $PrevLink %>
<a href="$PrevLink.ATT" class="previous">&laquo; <% _t('CommentsInterface_ss.PREV','previous') %></a>
<% end_if %>
<% if $Pages %><% loop $Pages %>
<% if $CurrentBool %>
<strong>$PageNum</strong>
<% else %>
<a href="$Link.ATT">$PageNum</a>
<% end_if %>
<% end_loop %><% end_if %>
<% if $NextLink %>
<a href="$NextLink.ATT" class="next"><% _t('CommentsInterface_ss.NEXT','next') %> &raquo;</a>
<% end_if %>
</p>
</div>
<% end_if %>

View File

@ -0,0 +1,25 @@
<% if $RepliesEnabled %>
<div class="comment-replies-container">
<% if $canPostComment %>
<div class="comment-reply-form-holder">
$ReplyForm
</div>
<% end_if %>
<div class="comment-replies-holder">
<% if $Replies %>
<ul class="comments-list level-{$Depth}">
<% loop $Replies %>
<li class="comment $EvenOdd<% if $FirstLast %> $FirstLast <% end_if %> $SpamClass">
<% include CommentsInterface_singlecomment %>
</li>
<% end_loop %>
</ul>
<% with $Replies %>
<% include ReplyPagination %>
<% end_with %>
<% end_if %>
</div>
</div>
<% end_if %>

View File

@ -1,3 +1,3 @@
<div class="pending-comment">
<p><% _t('CommentsInterface_pendingcomment_ss.AWAITINGMODERATION', 'Your comment has been submitted and is now awaiting moderation.') %></p>
</div>
</div>

View File

@ -0,0 +1,44 @@
<% if not $isPreview %>
<p class="info" id="$Permalink">
<% if $URL %>
<a class="author" href="$URL.URL" rel="nofollow">$AuthorName.XML</a>
<% else %>
<span class="author">$AuthorName.XML</span>
<% end_if %>
<span class="date">$Created.Nice ($Created.Ago)</span>
</p>
<% end_if %>
<% if $Gravatar %>
<img class="gravatar" src="$Gravatar.ATT" alt="Gravatar for $Name.ATT" title="Gravatar for $Name.ATT" />
<% end_if %>
<div class="comment-text<% if $Gravatar %> hasGravatar<% end_if %>" id="<% if $isPreview %>comment-preview<% else %>{$Permalink}-text<% end_if %>">
<p>$EscapedComment</p>
</div>
<% if not $isPreview %>
<% if $ApproveLink || $SpamLink || $HamLink || $DeleteLink || $RepliesEnabled %>
<div class="comment-action-links">
<div class="comment-moderation-options">
<% if $ApproveLink %>
<a href="$ApproveLink.ATT" class="approve"><%t CommentsInterface_singlecomment_ss.APPROVE "Approve it" %></a>
<% end_if %>
<% if $SpamLink %>
<a href="$SpamLink.ATT" class="spam"><%t CommentsInterface_singlecomment_ss.ISSPAM "Spam it" %></a>
<% end_if %>
<% if $HamLink %>
<a href="$HamLink.ATT" class="ham"><%t CommentsInterface_singlecomment_ss.ISNTSPAM "Not spam" %></a>
<% end_if %>
<% if $DeleteLink %>
<a href="$DeleteLink.ATT" class="delete"><%t CommentsInterface_singlecomment_ss.REMCOM "Reject it" %></a>
<% end_if %>
</div>
<% if $RepliesEnabled && $canPostComment %>
<button class="comment-reply-link" type="button" aria-controls="$ReplyForm.FormName" aria-expanded="false">
<%t CommentsInterface_singlecomment_ss.REPLYTO "Reply to" %> $AuthorName.XML
</button>
<% end_if %>
</div>
<% end_if %>
<% include CommentReplies %>
<% end_if %>

View File

@ -0,0 +1 @@
<% include CommentPagination %>

View File

@ -0,0 +1,64 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Comments\Admin\CommentAdmin;
use SilverStripe\Control\Session;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\i18n\i18n;
class CommentAdminTest extends SapphireTest
{
protected $usesDatabase = true;
public function testProvidePermissions()
{
$commentAdmin = new CommentAdmin();
$commentAdmin->getRequest()->setSession(new Session([]));
i18n::set_locale('fr');
$this->assertEquals(
'Accès au CMS',
$commentAdmin->providePermissions()['CMS_ACCESS_CommentAdmin']['category']
);
i18n::set_locale('en');
$expected = [
'CMS_ACCESS_CommentAdmin' => [
'name' => 'Access to \'Comments\' section',
'category' => 'CMS Access',
]
];
$this->assertEquals($expected, $commentAdmin->providePermissions());
}
public function testGetEditForm()
{
$commentAdmin = new CommentAdmin();
$commentAdmin->getRequest()->setSession(new Session([]));
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$form = $commentAdmin->getEditForm();
$names = $this->getFormFieldNames($form);
$expected = [
'NewComments',
'ApprovedComments',
'SpamComments',
];
$this->assertEquals($expected, $names);
$this->logOut();
}
private function getFormFieldNames($form)
{
$result = [];
$fields = $form->Fields();
$tab = $fields->findOrMakeTab('Root');
$fields = $tab->FieldList();
foreach ($fields as $field) {
array_push($result, $field->getName());
}
return $result;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\FieldGroup;
class CommentTestHelper implements TestOnly
{
/**
* This only works if the last section is not a field group, e.g. a Comments
* field group inside of a Root.Settings tab will not work
*/
public static function assertFieldsForTab($context, $tabName, $expected, $fields)
{
$tab = $fields->findOrMakeTab($tabName);
$fields = $tab->FieldList();
self::assertFieldNames($context, $expected, $fields);
}
public static function assertFieldNames($context, $expected, $fields)
{
$actual = array();
foreach ($fields as $field) {
array_push($actual, $field->getName());
}
$context->assertEquals($expected, $actual);
}
}

View File

@ -0,0 +1,338 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Akismet\AkismetSpamProtector;
use SilverStripe\Comments\Controllers\CommentingController;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Comments\Model\Comment\SecurityToken as CommentSecurityToken;
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Security\SecurityToken;
use SilverStripe\Security\Security;
class CommentingControllerTest extends FunctionalTest
{
/**
* {@inheritDoc}
*/
protected static $fixture_file = 'CommentsTest.yml';
/**
* {@inheritDoc}
*/
protected static $extra_dataobjects = [
CommentableItem::class
];
protected $securityEnabled;
protected function tearDown(): void
{
if ($this->securityEnabled) {
SecurityToken::inst()->enable();
} else {
SecurityToken::inst()->disable();
}
parent::tearDown();
}
protected function setUp(): void
{
parent::setUp();
$this->securityEnabled = SecurityToken::inst()->is_enabled();
// We will assert against explicit responses, unless handed otherwise in a test for redirects
$this->autoFollowRedirection = false;
// Mock Akismet if it's installed
if (class_exists(AkismetSpamProtector::class)) {
$akismetMock = $this->createMock(AkismetSpamProtector::class);
Injector::inst()->registerService($akismetMock, AkismetSpamProtector::class);
}
}
public function testCommentsFormUsePreview()
{
$parent = $this->objFromFixture(CommentableItem::class, 'first');
$commController = new CommentingController();
$commController->setOwnerRecord($parent);
$form = $commController->CommentsForm();
$commentsFields = $form->Fields()->first()->FieldList();
$expected = array('Name', 'Email', 'URL', 'Comment');
CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
// test with preview on
Config::modify()->merge(CommentableItem::class, 'comments', array(
'use_preview' => true
));
$parent = $this->objFromFixture(CommentableItem::class, 'first');
$commController = new CommentingController();
$commController->setOwnerRecord($parent);
$this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
SecurityToken::inst()->disable();
$this->autoFollowRedirection = false;
$form = $commController->CommentsForm();
$commentsFields = $form->Fields()->first()->FieldList();
$expected = array('Name', 'Email', 'URL', 'Comment', 'PreviewComment');
CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
}
public function testApproveUnmoderatedComment()
{
SecurityToken::inst()->disable();
// mark a comment as spam then approve it
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$comment = $this->objFromFixture(Comment::class, 'testModeratedComment1');
$st = new CommentSecurityToken($comment);
$url = 'comments/approve/' . $comment->ID;
$url = $st->addToUrl($url, Security::getCurrentUser());
$response = $this->get($url, null, ['Referer' => '/']);
$this->assertEquals(302, $response->getStatusCode());
$comment = DataObject::get_by_id(Comment::class, $comment->ID);
// Need to use 0,1 here instead of false, true for SQLite
$this->assertEquals(0, $comment->IsSpam);
$this->assertEquals(1, $comment->Moderated);
// try and approve a non existent comment
$response = $this->get('comments/approve/100000');
$this->assertEquals(404, $response->getStatusCode());
}
public function testSetGetOwnerController()
{
$commController = new CommentingController();
$commController->setOwnerController(Controller::curr());
$this->assertEquals(Controller::curr(), $commController->getOwnerController());
$commController->setOwnerController(null);
$this->assertNull($commController->getOwnerController());
}
public function testHam()
{
SecurityToken::inst()->disable();
// mark a comment as spam then ham it
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->markSpam();
$st = new CommentSecurityToken($comment);
$url = 'comments/ham/' . $comment->ID;
$url = $st->addToUrl($url, Security::getCurrentUser());
$response = $this->get($url);
$this->assertEquals(302, $response->getStatusCode());
$comment = DataObject::get_by_id(Comment::class, $comment->ID);
// Need to use 0,1 here instead of false, true for SQLite
$this->assertEquals(0, $comment->IsSpam);
$this->assertEquals(1, $comment->Moderated);
// try and ham a non existent comment
$response = $this->get('comments/ham/100000');
$this->assertEquals(404, $response->getStatusCode());
}
public function testSpam()
{
// mark a comment as approved then spam it
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->markApproved();
$st = new CommentSecurityToken($comment);
$url = 'comments/spam/' . $comment->ID;
$url = $st->addToUrl($url, Security::getCurrentUser());
$response = $this->get($url);
$this->assertEquals(302, $response->getStatusCode());
$comment = DataObject::get_by_id(Comment::class, $comment->ID);
// Need to use 0,1 here instead of false, true for SQLite
$this->assertEquals(1, $comment->IsSpam);
$this->assertEquals(1, $comment->Moderated);
// try and spam a non existent comment
$response = $this->get('comments/spam/100000');
$this->assertEquals(404, $response->getStatusCode());
}
public function testRSS()
{
// Delete the newly added children of firstComA so as not to have to recalculate values below
$this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
$item = $this->objFromFixture(CommentableItem::class, 'first');
// comments sitewide
$response = $this->get('comments/rss');
$comment = "10 approved, non spam comments on page 1";
$this->assertEquals(10, substr_count($response->getBody() ?? '', "<item>"), $comment);
$response = $this->get('comments/rss?start=10');
$this->assertEquals(
4,
substr_count($response->getBody() ?? '', "<item>"),
"3 approved, non spam comments on page 2"
);
// all comments on a type
$response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem');
$this->assertEquals(10, substr_count($response->getBody() ?? '', "<item>"));
$response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem?start=10');
$this->assertEquals(
4,
substr_count($response->getBody() ?? '', "<item>"),
"3 approved, non spam comments on page 2"
);
// specific page
$response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem/'.$item->ID);
$this->assertEquals(1, substr_count($response->getBody() ?? '', "<item>"));
$this->assertStringContainsString('<dc:creator>FA</dc:creator>', $response->getBody());
// test accessing comments on a type that doesn't exist
$response = $this->get('comments/rss/Fake');
$this->assertEquals(404, $response->getStatusCode());
}
// This is returning a 404 which looks logical code wise but also a bit weird.
// Test module on a clean install and check what the actual URL is first
/* public function testReply() {
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$comment = $this->objFromFixture('Comment', 'firstComA');
$item = $this->objFromFixture('CommentableItem', 'first');
$st = new CommentSecurityToken($comment);
$url = 'comments/reply/' . $item->ID.'?ParentCommentID=' . $comment->ID;
error_log($url);
$response = $this->get($url);
error_log(print_r($response,1));
$this->assertEquals(200, $response->getStatusCode());
}
*/
/*
public function testCommentsFormLoadMemberData() {
Config::modify()->set('CommentableItem', 'comments', array(
'use_preview' => false
));
$this->logInAs('visitor');
SecurityToken::inst()->disable();
$parent = $this->objFromFixture('CommentableItem', 'first');
$parent->CommentsRequireLogin = true;
$parent->PostingRequiredPermission = true;
//$parent->write();
$commController = new CommentingController();
$commController->setOwnerRecord($parent);
$form = $commController->CommentsForm();
$commentsFields = $form->Fields()->first()->FieldList();
$expected = array('Name', 'Email', 'URL', 'Comment', 'PreviewComment');
CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
}
*/
public function testCommentsForm()
{
$this->autoFollowRedirection = true;
// Delete the newly added children of firstComA so as not to change this test
$this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
$this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
SecurityToken::inst()->disable();
$this->autoFollowRedirection = false;
$parent = $this->objFromFixture(CommentableItem::class, 'first');
// Test posting to base comment
$response = $this->post(
'comments/CommentsForm',
array(
'Name' => 'Poster',
'Email' => 'guy@test.com',
'Comment' => 'My Comment',
'ParentID' => $parent->ID,
'ParentClassName' => CommentableItem::class,
'action_doPostComment' => 'Post'
)
);
$this->assertEquals(302, $response->getStatusCode());
// $this->assertStringStartsWith('CommentableItemController#comment-', $response->getHeader('Location'));
$this->assertListEquals(
array(
array(
'Name' => 'Poster',
'Email' => 'guy@test.com',
'Comment' => 'My Comment',
'ParentID' => $parent->ID,
'ParentClass' => CommentableItem::class,
)
),
Comment::get()->filter('Email', 'guy@test.com')
);
// Test posting to parent comment
$parentComment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(0, $parentComment->ChildComments()->count());
$response = $this->post(
'comments/reply/' . $parentComment->ID,
array(
'Name' => 'Test Author',
'Email' => 'test@test.com',
'Comment' => 'Making a reply to firstComA',
'ParentID' => $parent->ID,
'ParentClassName' => CommentableItem::class,
'ParentCommentID' => $parentComment->ID,
'action_doPostComment' => 'Post'
)
);
$this->assertEquals(302, $response->getStatusCode());
// $this->assertStringStartsWith('CommentableItemController#comment-', $response->getHeader('Location'));
$this->assertListEquals(
array(
array(
'Name' => 'Test Author',
'Email' => 'test@test.com',
'Comment' => 'Making a reply to firstComA',
'ParentID' => $parent->ID,
'ParentClass' => CommentableItem::class,
'ParentCommentID' => $parentComment->ID
)
),
$parentComment->ChildComments()
);
}
/**
* SS4 introduces namespaces. They don't work in URLs, so we encode and decode them here.
*/
public function testEncodeClassName()
{
$controller = new CommentingController;
$this->assertSame('SilverStripe-Comments-Model-Comment', $controller->encodeClassName(Comment::class));
}
public function testDecodeClassName()
{
$controller = new CommentingController;
$this->assertSame(Comment::class, $controller->decodeClassName('SilverStripe-Comments-Model-Comment'));
}
}

View File

@ -1,59 +0,0 @@
<?php
/**
* @package comments
* @subpackage tests
*/
class CommentingControllerTests extends FunctionalTest {
public static $fixture_file = 'comments/tests/CommentsTest.yml';
protected $extraDataObjects = array(
'CommentableItem'
);
public function setUp() {
parent::setUp();
Commenting::add('CommentableItem');
}
public function testRSS() {
$item = $this->objFromFixture('CommentableItem', 'first');
// comments sitewide
$response = $this->get('CommentingController/rss');
$this->assertEquals(10, substr_count($response->getBody(), "<item>"), "10 approved, non spam comments on page 1");
$response = $this->get('CommentingController/rss?start=10');
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
// all comments on a type
$response = $this->get('CommentingController/rss/CommentableItem');
$this->assertEquals(10, substr_count($response->getBody(), "<item>"));
$response = $this->get('CommentingController/rss/CommentableItem?start=10');
$this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
// specific page
$response = $this->get('CommentingController/rss/CommentableItem/'.$item->ID);
$this->assertEquals(1, substr_count($response->getBody(), "<item>"));
$this->assertContains('<dc:creator>FA</dc:creator>', $response->getBody());
// test accessing comments on a type that doesn't exist
$response = $this->get('CommentingController/rss/Fake');
$this->assertEquals(404, $response->getStatusCode());
}
public function testRSSSecuredCommentsForm() {
$this->markTestIncomplete("Not implemented");
}
public function testCommentsForm() {
$this->markTestIncomplete("Not implemented");
}
public function testDoCommentsForm() {
$this->markTestIncomplete("Not implemented");
}
}

View File

@ -0,0 +1,439 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Comments\Extensions\CommentsExtension;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Comments\Tests\CommentTestHelper;
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
use SilverStripe\Comments\Tests\Stubs\CommentableItemDisabled;
use SilverStripe\Comments\Tests\Stubs\CommentableItemEnabled;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Security\Member;
use SilverStripe\View\Requirements;
use SilverStripe\Security\Security;
class CommentsExtensionTest extends FunctionalTest
{
protected static $fixture_file = 'CommentsTest.yml';
protected static $disable_themes = true;
protected static $extra_dataobjects = [
CommentableItem::class,
CommentableItemEnabled::class,
CommentableItemDisabled::class,
];
protected static $required_extensions = [
CommentableItem::class => [
CommentsExtension::class,
],
];
protected function setUp(): void
{
parent::setUp();
// Set good default values
Config::modify()->merge(CommentsExtension::class, 'comments', [
'enabled' => true,
'enabled_cms' => false,
'require_login' => false,
'require_login_cms' => false,
'required_permission' => false,
'require_moderation_nonmembers' => false,
'require_moderation' => false,
'require_moderation_cms' => false,
'frontend_moderation' => false,
'Member' => false,
]);
// Configure this dataobject
Config::modify()->merge(CommentableItem::class, 'comments', [
'enabled_cms' => true
]);
}
public function testGetCommentsOption()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'comments_holder_id' => 'some-option'
]);
$item = $this->objFromFixture(CommentableItem::class, 'first');
$this->assertEquals('some-option', $item->getCommentsOption('comments_holder_id'));
}
public function testPopulateDefaults()
{
$this->markTestSkipped('TODO');
}
public function testUpdateSettingsFields()
{
$this->markTestSkipped('This needs SiteTree installed');
}
public function testGetModerationRequired()
{
// the 3 options take precedence in this order, executed if true
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_moderation_cms' => true,
'require_moderation' => true,
'require_moderation_nonmembers' => true
));
// With require moderation CMS set to true, the value of the field
// 'ModerationRequired' is returned
$item = $this->objFromFixture(CommentableItem::class, 'first');
$item->ModerationRequired = 'None';
$item->write();
$this->assertEquals('None', $item->getModerationRequired());
$item->ModerationRequired = 'Required';
$item->write();
$this->assertEquals('Required', $item->getModerationRequired());
$item->ModerationRequired = 'NonMembersOnly';
$item->write();
$this->assertEquals('NonMembersOnly', $item->getModerationRequired());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_moderation_cms' => false,
'require_moderation' => true,
'require_moderation_nonmembers' => true
));
$this->assertEquals('Required', $item->getModerationRequired());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_moderation_cms' => false,
'require_moderation' => false,
'require_moderation_nonmembers' => true
));
$this->assertEquals('NonMembersOnly', $item->getModerationRequired());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_moderation_cms' => false,
'require_moderation' => false,
'require_moderation_nonmembers' => false
));
$this->assertEquals('None', $item->getModerationRequired());
}
public function testGetCommentsRequireLogin()
{
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_login_cms' => true
));
// With require moderation CMS set to true, the value of the field
// 'ModerationRequired' is returned
$item = $this->objFromFixture(CommentableItem::class, 'first');
$item->CommentsRequireLogin = true;
$this->assertTrue($item->getCommentsRequireLogin());
$item->CommentsRequireLogin = false;
$this->assertFalse($item->getCommentsRequireLogin());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_login_cms' => false,
'require_login' => false
));
$this->assertFalse($item->getCommentsRequireLogin());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'require_login_cms' => false,
'require_login' => true
));
$this->assertTrue($item->getCommentsRequireLogin());
}
public function testAllComments()
{
$item = $this->objFromFixture(CommentableItem::class, 'first');
$this->assertEquals(4, $item->AllComments()->count());
}
public function testAllVisibleComments()
{
$this->logOut();
$item = $this->objFromFixture(CommentableItem::class, 'second');
$this->assertEquals(2, $item->AllVisibleComments()->count());
}
public function testComments()
{
Config::modify()->merge(CommentableItem::class, 'comments', array(
'nested_comments' => false
));
$item = $this->objFromFixture(CommentableItem::class, 'first');
$this->assertEquals(4, $item->Comments()->count());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'nested_comments' => true
));
$this->assertEquals(1, $item->Comments()->count());
}
public function testGetCommentsEnabled()
{
Config::modify()->merge(CommentableItem::class, 'comments', array(
'enabled_cms' => true
));
$item = $this->objFromFixture(CommentableItem::class, 'first');
$this->assertTrue($item->getCommentsEnabled());
$item->ProvideComments = 0;
$this->assertFalse($item->getCommentsEnabled());
}
public function testGetCommentHolderID()
{
$item = $this->objFromFixture(CommentableItem::class, 'first');
Config::modify()->merge(CommentableItem::class, 'comments', array(
'comments_holder_id' => 'commentid_test1',
));
$this->assertEquals('commentid_test1', $item->getCommentHolderID());
Config::modify()->merge(CommentableItem::class, 'comments', array(
'comments_holder_id' => 'commtentid_test_another',
));
$this->assertEquals('commtentid_test_another', $item->getCommentHolderID());
}
public function testGetPostingRequiredPermission()
{
$this->markTestSkipped('TODO');
}
public function testCanModerateComments()
{
// ensure nobody logged in
if (Security::getCurrentUser()) {
Security::getCurrentUser()->logOut();
}
$item = $this->objFromFixture(CommentableItem::class, 'first');
$this->assertFalse($item->canModerateComments());
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$this->assertTrue($item->canModerateComments());
}
public function testGetCommentRSSLink()
{
Config::modify()->merge('SilverStripe\\Control\\Director', 'alternate_base_url', 'http://unittesting.local');
$item = $this->objFromFixture(CommentableItem::class, 'first');
$link = $item->getCommentRSSLink();
$this->assertEquals('http://unittesting.local/comments/rss', $link);
}
public function testGetCommentRSSLinkPage()
{
Config::modify()->merge('SilverStripe\\Control\\Director', 'alternate_base_url', 'http://unittesting.local');
$item = $this->objFromFixture(CommentableItem::class, 'first');
$page = $item->getCommentRSSLinkPage();
$this->assertEquals(
'http://unittesting.local/comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem/' . $item->ID,
$page
);
}
public function testCommentsForm()
{
$this->logInWithPermission('ADMIN');
Config::modify()->merge(CommentableItem::class, 'comments', array(
'include_js' => false,
'comments_holder_id' => 'comments-holder',
));
$item = $this->objFromFixture(CommentableItem::class, 'first');
// The comments form is HTML to do assertions by contains
$cf = (string) $item->CommentsForm();
$expected = '/comments/CommentsForm/" method="POST" enctype="application/x-www-form-urlencoded">';
$this->assertStringContainsString($expected, $cf);
$this->assertStringContainsString('<h4>Post your comment</h4>', $cf);
// check the comments form exists
$expected = '<input type="text" name="Name"';
$this->assertStringContainsString($expected, $cf);
$expected = '<input type="email" name="Email"';
$this->assertStringContainsString($expected, $cf);
$expected = '<input type="text" name="URL"';
$this->assertStringContainsString($expected, $cf);
$expected = '<input type="hidden" name="ParentID"';
$this->assertStringContainsString($expected, $cf);
$expected = '<textarea name="Comment"';
$this->assertStringContainsString($expected, $cf);
$expected = '<input type="submit" name="action_doPostComment" value="Post" class="action"';
$this->assertStringContainsString($expected, $cf);
$expected = '/comments/spam/';
$this->assertStringContainsString($expected, $cf);
$expected = '<p>Reply to firstComA 1</p>';
$this->assertStringContainsString($expected, $cf);
$expected = '/comments/delete';
$this->assertStringContainsString($expected, $cf);
$expected = '<p>Reply to firstComA 2</p>';
$this->assertStringContainsString($expected, $cf);
$expected = '<p>Reply to firstComA 3</p>';
$this->assertStringContainsString($expected, $cf);
}
public function testAttachedToSiteTree()
{
$this->markTestSkipped('TODO');
}
public function testPagedComments()
{
$item = $this->objFromFixture(CommentableItem::class, 'first');
// Ensure Created times are set, as order not guaranteed if all set to 0
$comments = $item->PagedComments()->sort('ID');
$ctr = 0;
$timeBase = time()-10000;
foreach ($comments as $comment) {
$comment->Created = $timeBase + $ctr * 1000;
$comment->write();
$ctr++;
}
$results = $item->PagedComments()->toArray();
foreach ($results as $result) {
$result->sourceQueryParams = null;
}
$this->assertEquals(
$this->objFromFixture(Comment::class, 'firstComA')->Comment,
$results[3]->Comment
);
$this->assertEquals(
$this->objFromFixture(Comment::class, 'firstComAChild1')->Comment,
$results[2]->Comment
);
$this->assertEquals(
$this->objFromFixture(Comment::class, 'firstComAChild2')->Comment,
$results[1]->Comment
);
$this->assertEquals(
$this->objFromFixture(Comment::class, 'firstComAChild3')->Comment,
$results[0]->Comment
);
$this->assertEquals(4, sizeof($results ?? []));
}
public function testUpdateModerationFields()
{
$this->markTestSkipped('TODO');
}
public function testUpdateCMSFields()
{
Config::modify()->merge(
CommentableItem::class,
'comments',
array(
'require_login_cms' => false
)
);
$this->logInWithPermission('ADMIN');
$item = $this->objFromFixture(CommentableItem::class, 'first');
$item->ProvideComments = true;
$item->write();
$fields = $item->getCMSFields();
// print_r($item->getCMSFields());
CommentTestHelper::assertFieldsForTab(
$this,
'Root.Comments.CommentsNewCommentsTab',
array('NewComments'),
$fields
);
CommentTestHelper::assertFieldsForTab(
$this,
'Root.Comments.CommentsCommentsTab',
array('ApprovedComments'),
$fields
);
CommentTestHelper::assertFieldsForTab(
$this,
'Root.Comments.CommentsSpamCommentsTab',
array('SpamComments'),
$fields
);
Config::modify()->merge(
CommentableItem::class,
'comments',
array(
'require_login_cms' => true
)
);
$fields = $item->getCMSFields();
CommentTestHelper::assertFieldsForTab($this, 'Root.Settings', array('Comments'), $fields);
$settingsTab = $fields->findOrMakeTab('Root.Settings');
$settingsChildren = $settingsTab->getChildren();
$this->assertEquals(1, $settingsChildren->count());
$fieldGroup = $settingsChildren->first();
$fields = $fieldGroup->getChildren();
CommentTestHelper::assertFieldNames(
$this,
array('ProvideComments', 'CommentsRequireLogin'),
$fields
);
Config::modify()->merge(
CommentableItem::class,
'comments',
array(
'require_login_cms' => true,
'require_moderation_cms' => true
)
);
$fields = $item->getCMSFields();
CommentTestHelper::assertFieldsForTab(
$this,
'Root.Settings',
array('Comments', 'ModerationRequired'),
$fields
);
$settingsTab = $fields->findOrMakeTab('Root.Settings');
$settingsChildren = $settingsTab->getChildren();
$this->assertEquals(2, $settingsChildren->count());
$fieldGroup = $settingsChildren->first();
$fields = $fieldGroup->getChildren();
CommentTestHelper::assertFieldNames(
$this,
array('ProvideComments', 'CommentsRequireLogin'),
$fields
);
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Comments\Admin\CommentAdmin;
use SilverStripe\Comments\Admin\CommentsGridField;
use SilverStripe\Comments\Admin\CommentsGridFieldAction;
use SilverStripe\Comments\Admin\CommentsGridFieldConfig;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
use SilverStripe\Comments\Tests\Stubs\Team;
use SilverStripe\Control\Controller;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Dev\Deprecation;
class CommentsGridFieldActionTest extends SapphireTest
{
protected $usesDatabase = true;
protected static $extra_dataobjects = [
CommentableItem::class,
Team::class,
];
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
protected function setUp(): void
{
parent::setUp();
if (Deprecation::isEnabled()) {
$this->markTestSkipped('Test calls deprecated code');
}
$this->list = new DataList(Team::class);
$config = CommentsGridFieldConfig::create()->addComponent(new GridFieldDeleteAction());
$this->gridField = new CommentsGridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new CommentAdmin(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testAugmentColumns()
{
$action = new CommentsGridFieldAction();
// an entry called 'Actions' is added to the columns array
$columns = array();
$action->augmentColumns($this->gridField, $columns);
$expected = array('Actions');
$this->assertEquals($expected, $columns);
$columns = array('Actions');
$action->augmentColumns($this->gridField, $columns);
$expected = array('Actions');
$this->assertEquals($expected, $columns);
}
public function testGetColumnAttributes()
{
$action = new CommentsGridFieldAction();
$record = new Comment();
$attrs = $action->getColumnAttributes($this->gridField, $record, Comment::class);
$this->assertEquals(array('class' => 'col-buttons'), $attrs);
}
public function testGetColumnMetadata()
{
$action = new CommentsGridFieldAction();
$result = $action->getColumnMetadata($this->gridField, 'Actions');
$this->assertEquals(array('title' => ''), $result);
$result = $action->getColumnMetadata($this->gridField, 'SomethingElse');
$this->assertNull($result);
}
public function testGetColumnsHandled()
{
$action = new CommentsGridFieldAction();
$result = $action->getColumnsHandled($this->gridField);
$this->assertEquals(array('Actions'), $result);
}
public function testGetColumnContent()
{
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$action = new CommentsGridFieldAction();
$record = new Comment();
$record->Name = 'Name of commeter';
$record->Comment = 'This is a comment';
$record->write();
$recordID = $record->ID;
$html = $action->getColumnContent($this->gridField, $record, Comment::class);
$this->assertStringContainsString('data-url="admin/comments/mockform/field/testfield', $html);
$this->assertStringContainsString('value="Spam"', $html);
$this->assertStringContainsString('id="action_CustomAction' . $recordID . 'Spam"', $html);
$this->assertStringContainsString('value="Approve"', $html);
$this->assertStringContainsString('id="action_CustomAction' . $recordID . 'Approve"', $html);
// If marked as spam, only the approve button should be available
$record->markSpam();
$record->write();
$html = $action->getColumnContent($this->gridField, $record, Comment::class);
$this->assertStringContainsString('value="Approve"', $html);
$this->assertStringNotContainsString('value="Spam"', $html);
// If marked as spam, only the approve button should be available
$record->markApproved();
$record->write();
$html = $action->getColumnContent($this->gridField, $record, Comment::class);
$this->assertStringNotContainsString('value="Approve"', $html);
$this->assertStringContainsString('value="Spam"', $html);
}
public function testGetActions()
{
$action = new CommentsGridFieldAction();
$result = $action->getActions($this->gridField);
$this->assertEquals(array('spam', 'approve'), $result);
}
public function testHandleAction()
{
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$item = new CommentableItem;
$item->write();
$action = new CommentsGridFieldAction();
$record = new Comment();
$record->Name = 'Name of commenter';
$record->Comment = 'This is a comment';
$record->ParentID = $item->ID;
$record->ParentClass = $item->class;
$record->write();
$recordID = $record->ID;
$arguments = array('RecordID' => $recordID);
$data = array();
$result = $action->handleAction($this->gridField, 'spam', $arguments, $data);
$this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode());
$this->assertEquals(
'Comment marked as spam.',
Controller::curr()->getResponse()->getStatusDescription()
);
$record = DataObject::get_by_id(Comment::class, $recordID);
$this->assertEquals(1, $record->Moderated);
$this->assertEquals(1, $record->IsSpam);
//getStatusDescription
$result = $action->handleAction($this->gridField, 'approve', $arguments, $data);
$this->assertEquals(200, Controller::curr()->getResponse()->getStatusCode());
$this->assertEquals(
'Comment approved.',
Controller::curr()->getResponse()->getStatusDescription()
);
$record = DataObject::get_by_id(Comment::class, $recordID);
$this->assertEquals(1, $record->Moderated);
$this->assertEquals(0, $record->IsSpam);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Dev\SapphireTest;
class CommentsGridFieldBulkActionTest extends SapphireTest
{
public function testSpam()
{
$this->markTestSkipped('TODO');
}
public function testApprove()
{
$this->markTestSkipped('TODO');
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace SilverStripe\Comments\Tests;
use SilverStripe\Dev\SapphireTest;
class CommentsGridFieldConfigTest extends SapphireTest
{
public function testConstruct()
{
// $config = new CommentsGridFieldConfigTest();
$this->markTestSkipped('TODO');
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace SilverStripe\Comments\Tests;
use ReflectionClass;
use ReflectionException;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Comments\Admin\CommentsGridField;
use SilverStripe\Dev\SapphireTest;
class CommentsGridFieldTest extends SapphireTest
{
public function testNewRow()
{
$gridfield = new CommentsGridField('testfield', 'testfield');
// protected function newRow($total, $index, $record, $attributes, $content) {
$comment = new Comment();
$comment->Name = 'Fred Bloggs';
$comment->Comment = 'This is a comment';
$attr = array();
try {
$class = new ReflectionClass($gridfield);
$method = $class->getMethod('newRow');
$method->setAccessible(true);
} catch (ReflectionException $e) {
$this->fail($e->getMessage());
}
$params = array(1, 1, $comment, $attr, $comment->Comment);
$newRow = $method->invokeArgs($gridfield, $params);
$this->assertEquals('<tr>This is a comment</tr>', $newRow);
$attr = array('class' => 'cssClass');
$params = array(1, 1, $comment, $attr, $comment->Comment);
$newRow = $method->invokeArgs($gridfield, $params);
$this->assertEquals('<tr class="cssClass">This is a comment</tr>', $newRow);
$comment->markSpam();
$params = array(1, 1, $comment, $attr, $comment->Comment);
$newRow = $method->invokeArgs($gridfield, $params);
$this->assertEquals('<tr class="cssClass spam">This is a comment</tr>', $newRow);
}
}

View File

@ -1,296 +1,1233 @@
<?php
/**
* @package comments
*/
class CommentsTest extends FunctionalTest {
public static $fixture_file = 'comments/tests/CommentsTest.yml';
protected $extraDataObjects = array(
'CommentableItem'
);
namespace SilverStripe\Comments\Tests;
public function setUp() {
parent::setUp();
use HTMLPurifier;
use HTMLPurifier_Config;
use ReflectionClass;
use SilverStripe\Comments\Extensions\CommentsExtension;
use SilverStripe\Comments\Model\Comment;
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
use SilverStripe\Comments\Tests\Stubs\CommentableItemDisabled;
use SilverStripe\Comments\Tests\Stubs\CommentableItemEnabled;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
Commenting::add('CommentableItem');
}
class CommentsTest extends FunctionalTest
{
protected static $fixture_file = 'CommentsTest.yml';
public function testCommentsList() {
// comments don't require moderation so unmoderated comments can be
// shown but not spam posts
Commenting::set_config_value('CommentableItem','require_moderation', false);
protected static $extra_dataobjects = [
CommentableItem::class,
CommentableItemEnabled::class,
CommentableItemDisabled::class,
];
$item = $this->objFromFixture('CommentableItem', 'spammed');
protected function setUp(): void
{
parent::setUp();
$this->assertDOSEquals(array(
array('Name' => 'Comment 1'),
array('Name' => 'Comment 3')
), $item->getComments(), 'Only 2 non spam posts should be shown');
// Set good default values
Config::modify()->merge(CommentsExtension::class, 'comments', [
'enabled' => true,
'comment_permalink_prefix' => 'comment-',
]);
}
// when moderated, only moderated, non spam posts should be shown.
Commenting::set_config_value('CommentableItem','require_moderation', true);
public function testCommentsList()
{
// comments don't require moderation so unmoderated comments can be
// shown but not spam posts
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_moderation_nonmembers' => false,
'require_moderation' => false,
'require_moderation_cms' => false,
]);
$this->assertDOSEquals(array(
array('Name' => 'Comment 3')
), $item->getComments(), 'Only 1 non spam, moderated post should be shown');
$item = $this->objFromFixture(CommentableItem::class, 'spammed');
// when logged in as an user with CMS_ACCESS_CommentAdmin rights they
// should see all the comments whether we have moderation on or not
$this->logInWithPermission('CMS_ACCESS_CommentAdmin');
$this->assertListEquals([
['Name' => 'Comment 1'],
['Name' => 'Comment 3']
], $item->Comments(), 'Only 2 non spam posts should be shown');
Commenting::set_config_value('CommentableItem','require_moderation', true);
$this->assertEquals(4, $item->getComments()->Count());
// when moderated, only moderated, non spam posts should be shown.
Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => true]);
Commenting::set_config_value('CommentableItem','require_moderation', false);
$this->assertEquals(4, $item->getComments()->Count());
}
// Check that require_moderation overrides this option
Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => true]);
public function testCanView() {
$visitor = $this->objFromFixture('Member', 'visitor');
$admin = $this->objFromFixture('Member', 'commentadmin');
$comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertListEquals(array(
array('Name' => 'Comment 3')
), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
$this->assertEquals(1, $item->Comments()->Count());
$this->assertTrue($comment->canView($visitor),
'Unauthenticated members can view comments associated to a object with ProvideComments=1'
);
$this->assertTrue($comment->canView($admin),
'Admins with CMS_ACCESS_CommentAdmin permissions can view comments associated to a page with ProvideComments=1'
);
$disabledComment = $this->objFromFixture('Comment', 'disabledCom');
$this->assertFalse($disabledComment->canView($visitor),
'Unauthenticated members can not view comments associated to a object with ProvideComments=0'
);
// require_moderation_nonmembers still filters out unmoderated comments
Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => false]);
$this->assertEquals(1, $item->Comments()->Count());
$this->assertTrue($disabledComment->canView($admin),
'Admins with CMS_ACCESS_CommentAdmin permissions can view comments associated to a page with ProvideComments=0'
);
}
public function testCanEdit() {
$visitor = $this->objFromFixture('Member', 'visitor');
$admin = $this->objFromFixture('Member', 'commentadmin');
$comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertFalse($comment->canEdit($visitor));
$this->assertTrue($comment->canEdit($admin));
}
public function testCanDelete() {
$visitor = $this->objFromFixture('Member', 'visitor');
$admin = $this->objFromFixture('Member', 'commentadmin');
$comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertFalse($comment->canEdit($visitor));
$this->assertTrue($comment->canEdit($admin));
}
public function testDeleteComment() {
$comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
$delete = $this->get('CommentingController/delete/'.$comment->ID);
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertTrue($check && $check->exists());
Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => false]);
$this->assertEquals(2, $item->Comments()->Count());
$firstPage = $this->objFromFixture('CommentableItem', 'first');
$this->autoFollowRedirection = false;
$this->logInAs('commentadmin');
$firstComment = $this->objFromFixture('Comment', 'firstComA');
$firstCommentID = $firstComment->ID;
Director::test($firstPage->RelativeLink(), null, $this->session());
$delete = $this->get('CommentingController/delete/'.$firstComment->ID);
$check = DataObject::get_by_id('Comment', $firstCommentID);
$this->assertFalse($check && $check->exists());
}
// With unmoderated comments set to display in frontend
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_moderation' => true,
'frontend_moderation' => true,
]);
$this->assertEquals(1, $item->Comments()->Count());
public function testSpamComment() {
$comment = $this->objFromFixture('Comment', 'firstComA');
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
$spam = $this->get('CommentingController/spam/'.$comment->ID);
$this->logInWithPermission('ADMIN');
$this->assertEquals(2, $item->Comments()->Count());
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
// With spam comments set to display in frontend
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_moderation' => true,
'frontend_moderation' => false,
'frontend_spam' => true,
]);
$this->autoFollowRedirection = false;
$this->logInAs('commentadmin');
$this->logOut();
$this->assertEquals(1, $item->Comments()->Count());
$this->assertContains('CommentingController/spam/'. $comment->ID, $comment->SpamLink()->getValue());
$this->logInWithPermission('ADMIN');
$this->assertEquals(2, $item->Comments()->Count());
$spam = $this->get('CommentingController/spam/'.$comment->ID);
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(1, $check->IsSpam);
$this->assertNull($check->SpamLink());
}
// With spam and unmoderated comments set to display in frontend
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_moderation' => true,
'frontend_moderation' => true,
'frontend_spam' => true,
]);
public function testHamComment() {
$comment = $this->objFromFixture('Comment', 'secondComC');
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
$ham = $this->get('CommentingController/ham/'.$comment->ID);
$this->logOut();
$this->assertEquals(1, $item->Comments()->Count());
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
$this->logInWithPermission('ADMIN');
$this->assertEquals(4, $item->Comments()->Count());
}
$this->autoFollowRedirection = false;
$this->logInAs('commentadmin');
/**
* Test moderation options configured via the CMS
*/
public function testCommentCMSModerationList()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_moderation' => true,
'require_moderation_cms' => true,
]);
$this->assertContains('CommentingController/ham/'. $comment->ID, $comment->HamLink()->getValue());
$item = $this->objFromFixture(CommentableItem::class, 'spammed');
$ham = $this->get('CommentingController/ham/'.$comment->ID);
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(0, $check->IsSpam);
$this->assertEquals('None', $item->getModerationRequired());
$this->assertNull($check->HamLink());
}
public function testApproveComment() {
$comment = $this->objFromFixture('Comment', 'secondComB');
$this->assertNull($comment->ApproveLink(), 'No permission to see mark as approved link');
$ham = $this->get('CommentingController/approve/'.$comment->ID);
$this->assertListEquals([
['Name' => 'Comment 1'],
['Name' => 'Comment 3']
], $item->Comments(), 'Only 2 non spam posts should be shown');
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(0, $check->Moderated, 'No permission to mark as approved');
// when moderated, only moderated, non spam posts should be shown.
$item->ModerationRequired = 'NonMembersOnly';
$item->write();
$this->autoFollowRedirection = false;
$this->logInAs('commentadmin');
$this->assertEquals('NonMembersOnly', $item->getModerationRequired());
$this->assertContains('CommentingController/approve/'. $comment->ID, $comment->ApproveLink()->getValue());
// Check that require_moderation overrides this option
$item->ModerationRequired = 'Required';
$item->write();
$this->assertEquals('Required', $item->getModerationRequired());
$ham = $this->get('CommentingController/approve/'.$comment->ID);
$check = DataObject::get_by_id('Comment', $comment->ID);
$this->assertEquals(1, $check->Moderated);
$this->assertListEquals([
['Name' => 'Comment 3']
], $item->Comments(), 'Only 1 non spam, moderated post should be shown');
$this->assertEquals(1, $item->Comments()->Count());
$this->assertNull($check->ApproveLink());
}
// require_moderation_nonmembers still filters out unmoderated comments
$item->ModerationRequired = 'NonMembersOnly';
$item->write();
$this->assertEquals(1, $item->Comments()->Count());
public function testCommenterURLWrite() {
$comment = new Comment();
// We only care about the CommenterURL, so only set that
// Check a http and https URL. Add more test urls here as needed.
$protocols = array(
'Http',
'Https',
);
$url = '://example.com';
$item->ModerationRequired = 'None';
$item->write();
$this->assertEquals(2, $item->Comments()->Count());
}
foreach($protocols as $protocol) {
$comment->CommenterURL = $protocol . $url;
// The protocol should stay as if, assuming it is valid
$comment->write();
$this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
}
}
public function testCanPostComment()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_login' => false,
'require_login_cms' => false,
'required_permission' => false,
]);
/** @var CommentableItem&CommentsExtension $item */
$item = $this->objFromFixture(CommentableItem::class, 'first');
/** @var CommentableItem&CommentsExtension $item2 */
$item2 = $this->objFromFixture(CommentableItem::class, 'second');
public function testSanitizesWithAllowHtml() {
if(!class_exists('HTMLPurifier')) {
$this->markTestSkipped('HTMLPurifier class not found');
return;
}
// Test restriction free commenting
$this->logOut();
$this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item->canPostComment());
$origAllowed = Commenting::get_config_value('CommentableItem','html_allowed');
// Without HTML allowed
$comment1 = new Comment();
$comment1->BaseClass = 'CommentableItem';
$comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment1->write();
$this->assertEquals(
'<p><script>alert("w00t")</script>my comment</p>',
$comment1->Comment,
'Does not remove HTML tags with html_allowed=false, ' .
'which is correct behaviour because the HTML will be escaped'
);
// Test permission required to post
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_login' => true,
'required_permission' => 'POSTING_PERMISSION',
]);
$this->assertTrue($item->CommentsRequireLogin);
$this->assertFalse($item->canPostComment());
$this->logInWithPermission('WRONG_ONE');
$this->assertFalse($item->canPostComment());
$this->logInWithPermission('POSTING_PERMISSION');
$this->assertTrue($item->canPostComment());
$this->logInWithPermission('ADMIN');
$this->assertTrue($item->canPostComment());
// With HTML allowed
Commenting::set_config_value('CommentableItem','html_allowed', true);
$comment2 = new Comment();
$comment2->BaseClass = 'CommentableItem';
$comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment2->write();
$this->assertEquals(
'<p>my comment</p>',
$comment2->Comment,
'Removes HTML tags which are not on the whitelist'
);
// Test require login to post, but not any permissions
Config::modify()->merge(CommentableItem::class, 'comments', [
'required_permission' => false,
]);
$this->assertTrue($item->CommentsRequireLogin);
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
}
$this->logOut();
$this->assertFalse($item->canPostComment());
$this->logInWithPermission('ANY_PERMISSION');
$this->assertTrue($item->canPostComment());
public function testDefaultTemplateRendersHtmlWithAllowHtml() {
if(!class_exists('HTMLPurifier')) {
$this->markTestSkipped('HTMLPurifier class not found');
}
// Test options set via CMS
Config::modify()->merge(CommentableItem::class, 'comments', [
'require_login' => true,
'require_login_cms' => true,
]);
$this->assertFalse($item->CommentsRequireLogin);
$this->assertTrue($item2->CommentsRequireLogin);
$origAllowed = Commenting::get_config_value('CommentableItem', 'html_allowed');
$item = new CommentableItem();
$item->write();
$this->logOut();
$this->assertTrue($item->canPostComment());
$this->assertFalse($item2->canPostComment());
// Without HTML allowed
$comment = new Comment();
$comment->Comment = '<p>my comment</p>';
$comment->ParentID = $item->ID;
$comment->BaseClass = 'CommentableItem';
$comment->write();
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
$this->assertContains(
'&lt;p&gt;my comment&lt;/p&gt;',
$html
);
// Login grants permission to post
$this->logInWithPermission('ANY_PERMISSION');
$this->assertTrue($item->canPostComment());
$this->assertTrue($item2->canPostComment());
}
public function testDeleteComment()
{
// Test anonymous user
$this->logOut();
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$commentID = $comment->ID;
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
$delete = $this->get('comments/delete/' . $comment->ID . '?ajax=1');
$this->assertEquals(403, $delete->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertTrue($check && $check->exists());
Commenting::set_config_value('CommentableItem','html_allowed', true);
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
$this->assertContains(
'<p>my comment</p>',
$html
);
// Test non-authenticated user
$this->logInAs('visitor');
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
}
// Test authenticated user
$this->logInAs('commentadmin');
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$commentID = $comment->ID;
$adminComment1Link = $comment->DeleteLink();
$this->assertStringContainsString('comments/delete/' . $commentID . '?t=', $adminComment1Link);
}
/**
* @package comments
* @subpackage tests
*/
class CommentableItem extends DataObject implements TestOnly {
private static $db = array(
'ProvideComments' => 'Boolean',
'Title' => 'Varchar'
);
public function RelativeLink() {
return "CommentableItem_Controller";
}
public function canView($member = null) {
return true;
}
public function Link() {
return $this->RelativeLink();
}
public function AbsoluteLink() {
return Director::absoluteURL($this->RelativeLink());
}
}
/**
* @package comments
* @subpackage tests
*/
class CommentableItem_Controller extends Controller implements TestOnly {
public function index() {
return CommentableItem::get()->first()->CommentsForm();
}
// Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin2');
$delete = $this->get($adminComment1Link);
$this->assertEquals(400, $delete->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertTrue($check && $check->exists());
// Test that this other admin can delete the comment with their own link
$adminComment2Link = $comment->DeleteLink();
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->autoFollowRedirection = false;
$delete = $this->get($adminComment2Link);
$this->assertEquals(302, $delete->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertFalse($check && $check->exists());
}
public function testSpamComment()
{
// Test anonymous user
$this->logOut();
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$commentID = $comment->ID;
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
$spam = $this->get('comments/spam/' . $comment->ID . '?ajax=1');
$this->assertEquals(403, $spam->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
// Test non-authenticated user
$this->logInAs('visitor');
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
// Test authenticated user
$this->logInAs('commentadmin');
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$commentID = $comment->ID;
$adminComment1Link = $comment->SpamLink();
$this->assertStringContainsString('comments/spam/' . $commentID . '?t=', $adminComment1Link);
// Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin2');
$spam = $this->get($adminComment1Link);
$this->assertEquals(400, $spam->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $comment->ID);
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
// Test that this other admin can spam the comment with their own link
$adminComment2Link = $comment->SpamLink();
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->autoFollowRedirection = false;
$spam = $this->get($adminComment2Link);
$this->assertEquals(302, $spam->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(1, $check->IsSpam);
// Cannot re-spam spammed comment
$this->assertNull($check->SpamLink());
}
public function testHamComment()
{
// Test anonymous user
$this->logOut();
$comment = $this->objFromFixture(Comment::class, 'secondComC');
$commentID = $comment->ID;
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
$ham = $this->get('comments/ham/' . $comment->ID . '?ajax=1');
$this->assertEquals(403, $ham->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
// Test non-authenticated user
$this->logInAs('visitor');
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
// Test authenticated user
$this->logInAs('commentadmin');
$comment = $this->objFromFixture(Comment::class, 'secondComC');
$commentID = $comment->ID;
$adminComment1Link = $comment->HamLink();
$this->assertStringContainsString('comments/ham/' . $commentID . '?t=', $adminComment1Link);
// Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin2');
$ham = $this->get($adminComment1Link);
$this->assertEquals(400, $ham->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $comment->ID);
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
// Test that this other admin can ham the comment with their own link
$adminComment2Link = $comment->HamLink();
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->autoFollowRedirection = false;
$ham = $this->get($adminComment2Link);
$this->assertEquals(302, $ham->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(0, $check->IsSpam);
// Cannot re-ham hammed comment
$this->assertNull($check->HamLink());
}
public function testApproveComment()
{
// Test anonymous user
$this->logOut();
$comment = $this->objFromFixture(Comment::class, 'secondComB');
$commentID = $comment->ID;
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
$approve = $this->get('comments/approve/' . $comment->ID . '?ajax=1');
$this->assertEquals(403, $approve->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
// Test non-authenticated user
$this->logInAs('visitor');
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
// Test authenticated user
$this->logInAs('commentadmin');
$comment = $this->objFromFixture(Comment::class, 'secondComB');
$commentID = $comment->ID;
$adminComment1Link = $comment->ApproveLink();
$this->assertStringContainsString('comments/approve/' . $commentID . '?t=', $adminComment1Link);
// Test that this link can't be shared / XSS exploited
$this->logInAs('commentadmin2');
$approve = $this->get($adminComment1Link);
$this->assertEquals(400, $approve->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $comment->ID);
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
// Test that this other admin can approve the comment with their own link
$adminComment2Link = $comment->ApproveLink();
$this->assertNotEquals($adminComment2Link, $adminComment1Link);
$this->autoFollowRedirection = false;
$approve = $this->get($adminComment2Link);
$this->assertEquals(302, $approve->getStatusCode());
$check = DataObject::get_by_id(Comment::class, $commentID);
$this->assertEquals(1, $check->Moderated);
// Cannot re-approve approved comment
$this->assertNull($check->ApproveLink());
}
public function testCommenterURLWrite()
{
$comment = new Comment();
// We only care about the CommenterURL, so only set that
// Check a http and https URL. Add more test urls here as needed.
$protocols = [
'Http',
'Https',
];
$url = '://example.com';
foreach ($protocols as $protocol) {
$comment->CommenterURL = $protocol . $url;
// The protocol should stay as if, assuming it is valid
$comment->write();
$this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
}
}
public function testSanitizesWithAllowHtml()
{
if (!class_exists('\\HTMLPurifier')) {
$this->markTestSkipped('HTMLPurifier class not found');
}
// Add p for paragraph
// NOTE: The config method appears to append to the existing array
Config::modify()->merge(CommentableItem::class, 'comments', [
'html_allowed_elements' => ['p'],
]);
// Without HTML allowed
$comment1 = new Comment();
$comment1->AllowHtml = false;
$comment1->ParentClass = CommentableItem::class;
$comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment1->write();
$this->assertEquals(
'<p><script>alert("w00t")</script>my comment</p>',
$comment1->Comment,
'Does not remove HTML tags with html_allowed=false, ' .
'which is correct behaviour because the HTML will be escaped'
);
// With HTML allowed
$comment2 = new Comment();
$comment2->AllowHtml = true;
$comment2->ParentClass = CommentableItem::class;
$comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
$comment2->write();
$this->assertEquals(
'<p>my comment</p>',
$comment2->Comment,
'Removes HTML tags which are not on the whitelist'
);
}
public function testDefaultTemplateRendersHtmlWithAllowHtml()
{
if (!class_exists('\\HTMLPurifier')) {
$this->markTestSkipped('HTMLPurifier class not found');
}
Config::modify()->merge(CommentableItem::class, 'comments', [
'html_allowed_elements' => ['p'],
]);
$item = new CommentableItem();
$item->write();
// Without HTML allowed
$comment = new Comment();
$comment->Comment = '<p>my comment</p>';
$comment->AllowHtml = false;
$comment->ParentID = $item->ID;
$comment->ParentClass = CommentableItem::class;
$comment->write();
$html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface');
$this->assertStringContainsString(
'&lt;p&gt;my comment&lt;/p&gt;',
$html
);
$comment->AllowHtml = true;
$comment->write();
$html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface');
$this->assertStringContainsString(
'<p>my comment</p>',
$html
);
}
/**
* Tests whether comments are enabled or disabled by default
*/
public function testDefaultEnabled()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'enabled_cms' => true,
'require_moderation_cms' => true,
'require_login_cms' => true,
]);
// With default = true
$obj = new CommentableItem();
$this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled");
$this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemEnabled();
$this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemDisabled();
$this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin);
// With default = false
// Because of config rules about falsey values, apply config to object directly
Config::modify()->merge(CommentableItem::class, 'comments', [
'enabled' => false,
'require_login' => true,
'require_moderation' => true,
]);
$obj = new CommentableItem();
$this->assertFalse((bool)$obj->getCommentsOption('enabled'), 'Default setting is disabled');
$this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemEnabled();
$this->assertTrue((bool)$obj->ProvideComments);
$this->assertEquals('Required', $obj->ModerationRequired);
$this->assertTrue((bool)$obj->CommentsRequireLogin);
$obj = new CommentableItemDisabled();
$this->assertFalse((bool)$obj->ProvideComments);
$this->assertEquals('None', $obj->ModerationRequired);
$this->assertFalse((bool)$obj->CommentsRequireLogin);
}
public function testOnBeforeDelete()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$child = new Comment();
$child->Name = 'Fred Bloggs';
$child->Comment = 'Child of firstComA';
$child->write();
$comment->ChildComments()->add($child);
$this->assertEquals(4, $comment->ChildComments()->count());
$commentID = $comment->ID;
$childCommentID = $child->ID;
$comment->delete();
// assert that the new child been deleted
$this->assertNull(DataObject::get_by_id(Comment::class, $commentID));
$this->assertNull(DataObject::get_by_id(Comment::class, $childCommentID));
}
public function testRequireDefaultRecords()
{
$this->markTestSkipped('TODO');
}
public function testLink()
{
$comment = $this->objFromFixture(Comment::class, 'thirdComD');
$this->assertEquals(
'CommentableItemController#comment-' . $comment->ID,
$comment->Link()
);
$this->assertEquals($comment->ID, $comment->ID);
// An orphan comment has no link
$comment->ParentID = 0;
$comment->ParentClass = null;
$comment->write();
$this->assertEquals('', $comment->Link());
}
public function testPermalink()
{
$comment = $this->objFromFixture(Comment::class, 'thirdComD');
$this->assertEquals('comment-' . $comment->ID, $comment->Permalink());
}
/**
* Test field labels are defined
*/
public function testFieldLabels()
{
/** @var Comment $comment */
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$labels = $comment->FieldLabels();
$expected = [
'Name',
'Comment',
'Email',
'URL',
'IsSpam',
'Moderated',
'ParentTitle',
'Created',
];
foreach ($expected as $key) {
$this->assertArrayHasKey($key, $labels);
}
}
public function testGetParent()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$item = $this->objFromFixture(CommentableItem::class, 'first');
$parent = $comment->Parent();
$this->assertSame($item->getClassName(), $parent->getClassName());
$this->assertSame($item->ID, $parent->ID);
}
public function testGetParentTitle()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$title = $comment->getParentTitle();
$this->assertEquals('First', $title);
// Title from a comment with no parent is blank
$comment->ParentID = 0;
$comment->ParentClass = null;
$comment->write();
$this->assertEquals('', $comment->getParentTitle());
}
public function testGetParentClassName()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$className = $comment->getParentClassName();
$this->assertEquals(CommentableItem::class, $className);
}
public function testCastingHelper()
{
$this->markTestSkipped('TODO');
}
public function testGetEscapedComment()
{
$this->markTestSkipped('TODO');
}
public function testIsPreview()
{
$comment = new Comment();
$comment->Name = 'Fred Bloggs';
$comment->Comment = 'this is a test comment';
$this->assertTrue($comment->isPreview());
$comment->write();
$this->assertFalse($comment->isPreview());
}
public function testCanCreate()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
// admin can create - this is always false
$this->logInAs('commentadmin');
$this->assertFalse($comment->canCreate());
// visitor can view
$this->logInAs('visitor');
$this->assertFalse($comment->canCreate());
}
public function testCanView()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
// admin can view
$this->logInAs('commentadmin');
$this->assertTrue($comment->canView());
// visitor can view
$this->logInAs('visitor');
$this->assertTrue($comment->canView());
$comment->ParentID = 0;
$comment->write();
$this->assertFalse($comment->canView());
}
public function testCanEdit()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
// admin can edit
$this->logInAs('commentadmin');
$this->assertTrue($comment->canEdit());
// visitor cannot
$this->logInAs('visitor');
$this->assertFalse($comment->canEdit());
$comment->ParentID = 0;
$comment->write();
$this->assertFalse($comment->canEdit());
}
public function testCanDelete()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
// admin can delete
$this->logInAs('commentadmin');
$this->assertTrue($comment->canDelete());
// visitor cannot
$this->logInAs('visitor');
$this->assertFalse($comment->canDelete());
$comment->ParentID = 0;
$comment->write();
$this->assertFalse($comment->canDelete());
}
public function testGetMember()
{
$this->logInAs('visitor');
$current = Security::getCurrentUser();
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$method = $this->getMethod('getMember');
// null case
$member = $method->invokeArgs($comment, array());
$this->assertEquals($current->ID, $member->ID);
// numeric ID case
$member = $method->invokeArgs($comment, array($current->ID));
$this->assertEquals($current->ID, $member->ID);
// identity case
$member = $method->invokeArgs($comment, array($current));
$this->assertEquals($current->ID, $member->ID);
}
public function testGetAuthorName()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
'FA',
$comment->getAuthorName()
);
$comment->Name = '';
$this->assertEquals(
'',
$comment->getAuthorName()
);
$author = $this->objFromFixture(Member::class, 'visitor');
$comment->AuthorID = $author->ID;
$comment->write();
$this->assertEquals(
'visitor',
$comment->getAuthorName()
);
// null the names, expect null back
$comment->Name = null;
$comment->AuthorID = 0;
$this->assertNull($comment->getAuthorName());
}
public function testLinks()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->logInAs('commentadmin');
$method = $this->getMethod('ActionLink');
// test with starts of strings and tokens and salts change each time
$this->assertStringContainsString(
'/comments/theaction/' . $comment->ID,
$method->invokeArgs($comment, array('theaction'))
);
$this->assertStringContainsString(
'/comments/delete/' . $comment->ID,
$comment->DeleteLink()
);
$this->assertStringContainsString(
'/comments/spam/' . $comment->ID,
$comment->SpamLink()
);
$comment->markSpam();
$this->assertStringContainsString(
'/comments/ham/' . $comment->ID,
$comment->HamLink()
);
//markApproved
$comment->markUnapproved();
$this->assertStringContainsString(
'/comments/approve/' . $comment->ID,
$comment->ApproveLink()
);
}
public function testMarkSpam()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->markSpam();
$this->assertTrue($comment->Moderated);
$this->assertTrue($comment->IsSpam);
}
public function testMarkApproved()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->markApproved();
$this->assertTrue($comment->Moderated);
$this->assertFalse($comment->IsSpam);
}
public function testMarkUnapproved()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->markApproved();
$this->assertTrue($comment->Moderated);
}
public function testSpamClass()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals('notspam', $comment->spamClass());
$comment->Moderated = false;
$this->assertEquals('unmoderated', $comment->spamClass());
$comment->IsSpam = true;
$this->assertEquals('spam', $comment->spamClass());
}
public function testGetTitle()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
'Comment by FA on First',
$comment->getTitle()
);
}
public function testGetCMSFields()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$fields = $comment->getCMSFields();
$names = [];
foreach ($fields as $field) {
$names[] = $field->getName();
}
$expected = [
'Created',
'Name',
'Comment',
'Email',
'URL',
'Options',
];
$this->assertEquals($expected, $names);
}
public function testGetCMSFieldsCommentHasAuthor()
{
$member = Member::get()->filter('FirstName', 'visitor')->first();
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->AuthorID = $member->ID;
$comment->write();
$fields = $comment->getCMSFields();
$names = [];
foreach ($fields as $field) {
$names[] = $field->getName();
}
$expected = [
'Created',
'Name',
'AuthorMember',
'Comment',
'Email',
'URL',
'Options',
];
$this->assertEquals($expected, $names);
}
public function testGetCMSFieldsWithParentComment()
{
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$child = new Comment();
$child->Name = 'John Smith';
$child->Comment = 'This is yet another test commnent';
$child->ParentCommentID = $comment->ID;
$child->write();
$fields = $child->getCMSFields();
$names = [];
foreach ($fields as $field) {
$names[] = $field->getName();
}
$expected = [
'Created',
'Name',
'Comment',
'Email',
'URL',
'Options',
'ParentComment_Title',
'ParentComment_Created',
'ParentComment_AuthorName',
'ParentComment_EscapedComment',
];
$this->assertEquals($expected, $names);
}
public function testPurifyHtml()
{
if (!class_exists(HTMLPurifier_Config::class)) {
$this->markTestSkipped('HTMLPurifier class not found');
}
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$dirtyHTML = '<p><script>alert("w00t")</script>my comment</p>';
$this->assertEquals(
'my comment',
$comment->purifyHtml($dirtyHTML)
);
}
public function testGravatar()
{
// Turn gravatars on
Config::modify()->merge(CommentableItem::class, 'comments', [
'use_gravatar' => true,
'gravatar_size' => 80,
'gravatar_default' => 'identicon',
'gravatar_rating' => 'g',
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
'https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s'
. '=80&d=identicon&r=g',
$comment->Gravatar()
);
// Turn gravatars off
Config::modify()->merge(CommentableItem::class, 'comments', [
'use_gravatar' => false,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
'',
$comment->Gravatar()
);
}
public function testGetRepliesEnabled()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => false,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertFalse($comment->getRepliesEnabled());
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
]);
$this->assertTrue($comment->getRepliesEnabled());
$comment->Depth = 4;
$this->assertFalse($comment->getRepliesEnabled());
// 0 indicates no limit for nested_depth
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 0,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$comment->Depth = 234;
$comment->markUnapproved();
$this->assertFalse($comment->getRepliesEnabled());
$comment->markSpam();
$this->assertFalse($comment->getRepliesEnabled());
$comment->markApproved();
$this->assertTrue($comment->getRepliesEnabled());
}
public function testAllReplies()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
3,
$comment->allReplies()->count()
);
$child = new Comment();
$child->Name = 'Fred Smith';
$child->Comment = 'This is a child comment';
$child->ParentCommentID = $comment->ID;
// spam should be returned by this method
$child->markSpam();
$child->write();
$replies = $comment->allReplies();
$this->assertEquals(
4,
$comment->allReplies()->count()
);
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => false,
]);
$this->assertEquals(0, $comment->allReplies()->count());
}
public function testReplies()
{
CommentableItem::add_extension(CommentsExtension::class);
$this->logInWithPermission('ADMIN');
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$this->assertEquals(
3,
$comment->Replies()->count()
);
// Test that spam comments are not returned
$childComment = $comment->Replies()->first();
$childComment->IsSpam = 1;
$childComment->write();
$this->assertEquals(
2,
$comment->Replies()->count()
);
// Test that unmoderated comments are not returned
//
$childComment = $comment->Replies()->first();
// FIXME - moderation settings scenarios need checked here
$childComment->Moderated = 0;
$childComment->IsSpam = 0;
$childComment->write();
$this->assertEquals(
2,
$comment->Replies()->count()
);
// Test moderation required on the front end
$item = $this->objFromFixture(CommentableItem::class, 'first');
$item->ModerationRequired = 'Required';
$item->write();
Config::modify()->merge(CommentableItemDisabled::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
'frontend_moderation' => true,
]);
$comment = DataObject::get_by_id(Comment::class, $comment->ID);
$this->assertEquals(
2,
$comment->Replies()->count()
);
// Turn off nesting, empty array should be returned
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => false,
]);
$this->assertEquals(
0,
$comment->Replies()->count()
);
CommentableItem::remove_extension(CommentsExtension::class);
}
public function testPagedReplies()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
'comments_per_page' => 2,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$pagedList = $comment->pagedReplies();
$this->assertEquals(
2,
$pagedList->TotalPages()
);
$this->assertEquals(
3,
$pagedList->getTotalItems()
);
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => false,
]);
$this->assertEquals(0, $comment->PagedReplies()->count());
}
public function testReplyForm()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => false,
'nested_depth' => 4,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
// No nesting, no reply form
$form = $comment->replyForm();
$this->assertNull($form);
// parent item so show form
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
]);
$form = $comment->ReplyForm();
$this->assertNotNull($form);
$names = [];
foreach ($form->Fields() as $field) {
array_push($names, $field->getName());
}
$this->assertContains('NameEmailURLComment', $names, 'The CompositeField name');
$this->assertContains('ParentID', $names);
$this->assertContains('ParentClassName', $names);
$this->assertContains('ReturnURL', $names);
$this->assertContains('ParentCommentID', $names);
// no parent, no reply form
$comment->ParentID = 0;
$comment->ParentClass = null;
$comment->write();
$form = $comment->replyForm();
$this->assertNull($form);
}
public function testUpdateDepth()
{
Config::modify()->merge(CommentableItem::class, 'comments', [
'nested_comments' => true,
'nested_depth' => 4,
]);
$comment = $this->objFromFixture(Comment::class, 'firstComA');
$children = $comment->allReplies()->toArray();
// Make the second child a child of the first
// Make the third child a child of the second
$reply1 = $children[0];
$reply2 = $children[1];
$reply3 = $children[2];
$reply2->ParentCommentID = $reply1->ID;
$reply2->write();
$this->assertEquals(3, $reply2->Depth);
$reply3->ParentCommentID = $reply2->ID;
$reply3->write();
$this->assertEquals(4, $reply3->Depth);
}
public function testGetToken()
{
$this->markTestSkipped('TODO');
}
public function testMemberSalt()
{
$this->markTestSkipped('TODO');
}
public function testAddToUrl()
{
$this->markTestSkipped('TODO');
}
public function testCheckRequest()
{
$this->markTestSkipped('TODO');
}
public function testGenerate()
{
$this->markTestSkipped('TODO');
}
protected static function getMethod($name)
{
$class = new ReflectionClass(Comment::class);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
}

View File

@ -1,167 +1,192 @@
Member:
commentadmin:
FirstName: admin
visitor:
FirstName: visitor
SilverStripe\Security\Member:
commentadmin:
FirstName: admin
commentadmin2:
FirstName: admin2
visitor:
FirstName: visitor
Group:
commentadmins:
Title: Admin
Members: =>Member.commentadmin
SilverStripe\Security\Group:
commentadmins:
Title: Admin
Members: =>SilverStripe\Security\Member.commentadmin, =>SilverStripe\Security\Member.commentadmin2
Permission:
admin:
Code: CMS_ACCESS_CommentAdmin
Group: =>Group.commentadmins
SilverStripe\Security\Permission:
admin:
Code: CMS_ACCESS_CommentAdmin
Group: =>SilverStripe\Security\Group.commentadmins
CommentableItem:
first:
Title: First
ProvideComments: 1
second:
Title: Second
ProvideComments: 1
third:
Title: Third
ProvideComments: 1
nocomments:
Title: No comments
ProvideComments: 0
spammed:
ProvideComments: 1
Title: spammed
SilverStripe\Comments\Tests\Stubs\CommentableItem:
first:
Title: First
ProvideComments: 1
CommentsRequireLogin: 0
second:
Title: Second
ProvideComments: 1
CommentsRequireLogin: 1
third:
Title: Third
ProvideComments: 1
nocomments:
Title: No comments
ProvideComments: 0
spammed:
ProvideComments: 1
Title: spammed
ModerationRequired: 'None'
moderated:
ProvideComments: 1
Title: Moderate everything
ModerationRequired: Required
CommentsRequireLogin: 0
Comment:
firstComA:
ParentID: =>CommentableItem.first
Name: FA
Comment: textFA
BaseClass: CommentableItem
Moderated: 1
IsSpam: 0
secondComA:
ParentID: =>CommentableItem.second
Name: SA
Comment: textSA
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
secondComB:
ParentID: =>CommentableItem.second
Name: SB
Comment: textSB
Moderated: 0
IsSpam: 0
BaseClass: CommentableItem
secondComC:
ParentID: =>CommentableItem.second
Name: SB
Comment: textSB
Moderated: 1
IsSpam: 1
BaseClass: CommentableItem
thirdComA:
ParentID: =>CommentableItem.third
Name: TA
Comment: textTA
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComB:
ParentID: =>CommentableItem.third
Name: TB
Comment: textTB
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComC:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComD:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
BaseClass: CommentableItem
thirdComE:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
BaseClass: CommentableItem
thirdComF:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComG:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComH:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComI:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComJ:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
thirdComK:
ParentID: =>CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
disabledCom:
ParentID: =>CommentableItem.nocomments
Name: Disabled
Moderated: 0
IsSpam: 1
BaseClass: CommentableItem
testCommentList1:
ParentID: =>CommentableItem.spammed
Name: Comment 1
Moderated: 0
IsSpam: 0
BaseClass: CommentableItem
testCommentList2:
ParentID: =>CommentableItem.spammed
Name: Comment 2
Moderated: 1
IsSpam: 1
BaseClass: CommentableItem
testCommentList3:
ParentID: =>CommentableItem.spammed
Name: Comment 3
Moderated: 1
IsSpam: 0
BaseClass: CommentableItem
testCommentList4:
ParentID: =>CommentableItem.spammed
Name: Comment 4
Moderated: 0
IsSpam: 1
BaseClass: CommentableItem
SilverStripe\Comments\Model\Comment:
firstComA:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.first
Name: FA
Comment: textFA
Moderated: 1
IsSpam:
Depth: 1
firstComAChild1:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.first
ParentComment: =>SilverStripe\Comments\Model\Comment.firstComA
Name: John Smith
Comment: Reply to firstComA 1
Moderated: 1
IsSpam: 0
Depth: 2
firstComAChild2:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.first
ParentComment: =>SilverStripe\Comments\Model\Comment.firstComA
Name: John Smith
Comment: Reply to firstComA 2
Moderated: 1
IsSpam: 0
Depth: 2
firstComAChild3:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.first
ParentComment: =>SilverStripe\Comments\Model\Comment.firstComA
Name: John Smith
Comment: Reply to firstComA 3
Moderated: 1
IsSpam: 0
Depth: 2
secondComA:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
Name: SA
Comment: textSA
Moderated: 1
IsSpam: 0
secondComB:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
Name: SB
Comment: textSB
Moderated: 0
IsSpam: 0
secondComC:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
Name: SB
Comment: textSB
Moderated: 1
IsSpam: 1
thirdComA:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TA
Comment: textTA
Moderated: 1
IsSpam: 0
thirdComB:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TB
Comment: textTB
Moderated: 1
IsSpam: 0
thirdComC:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComD:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
thirdComE:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
thirdComF:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComG:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComH:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComI:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComJ:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
thirdComK:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
Name: TC
Comment: textTC
Moderated: 1
IsSpam: 0
disabledCom:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.nocomments
Name: Disabled
Moderated: 0
IsSpam: 1
testCommentList1:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
Name: Comment 1
Moderated: 0
IsSpam: 0
testCommentList2:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
Name: Comment 2
Moderated: 1
IsSpam: 1
testCommentList3:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
Name: Comment 3
Moderated: 1
IsSpam: 0
testCommentList4:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
Name: Comment 4
Moderated: 0
IsSpam: 1
testModeratedComment1:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.moderated
Name: Moderated comment 1
Moderated: 0
IsSpam: 0
testModeratedComment2:
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.moderated
Name: Moderated comment 2
Moderated: 0
IsSpam: 1

View File

@ -0,0 +1,19 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class Cheerleader extends DataObject implements TestOnly
{
private static $table_name = 'CommentsTest_Cheerleader';
private static $db = [
'Name' => 'Varchar',
];
private static $has_one = [
'Team' => Team::class,
];
}

View File

@ -0,0 +1,62 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
use SilverStripe\Comments\Extensions\CommentsExtension;
use SilverStripe\Control\Director;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
class CommentableItem extends DataObject implements TestOnly
{
private static $db = array(
'Title' => 'Varchar'
);
private static $extensions = array(
CommentsExtension::class
);
private static $table_name = 'CommentableItem';
public function RelativeLink()
{
return 'CommentableItemController';
}
public function canView($member = null)
{
return true;
}
// This is needed for canModerateComments
public function canEdit($member = null)
{
if ($member instanceof Member) {
$memberID = $member->ID;
} elseif (is_numeric($member)) {
$memberID = $member;
} else {
$currentUser = Security::getCurrentUser();
$memberID = $currentUser ? $currentUser->ID : 0;
}
if ($memberID && Permission::checkMember($memberID, array('ADMIN', 'CMS_ACCESS_CommentAdmin'))) {
return true;
}
return false;
}
public function Link()
{
return $this->RelativeLink();
}
public function AbsoluteLink()
{
return Director::absoluteURL($this->RelativeLink());
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
use SilverStripe\Control\Controller;
use SilverStripe\Dev\TestOnly;
/**
* @package comments
* @subpackage tests
*/
class CommentableItemController extends Controller implements TestOnly
{
public function index()
{
return CommentableItem::get()->first()->CommentsForm();
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
class CommentableItemDisabled extends CommentableItem
{
private static $defaults = array(
'ProvideComments' => false,
'ModerationRequired' => 'None',
'CommentsRequireLogin' => false
);
private static $table_name = 'CommentableItemDisabled';
}

View File

@ -0,0 +1,14 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
class CommentableItemEnabled extends CommentableItem
{
private static $defaults = array(
'ProvideComments' => true,
'ModerationRequired' => 'Required',
'CommentsRequireLogin' => true
);
private static $table_name = 'CommentableItemEnabled';
}

24
tests/Stubs/Player.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ManyManyList;
/**
* @method ManyManyList Teams()
*/
class Player extends DataObject implements TestOnly
{
private static $table_name = 'CommentsTest_Player';
private static $db = array(
'Name' => 'Varchar',
'Email' => 'Varchar',
);
private static $belongs_many_many = [
'Teams' => Team::class,
];
}

35
tests/Stubs/Team.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace SilverStripe\Comments\Tests\Stubs;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class Team extends DataObject implements TestOnly
{
private static $table_name = 'CommentsTest_Team';
private static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar',
);
private static $many_many = [
'Players' => Player::class,
];
private static $has_many = [
'Cheerleaders' => Cheerleader::class,
];
private static $searchable_fields = [
'Name',
'City',
'Cheerleaders.Name',
];
public function canView($member = null)
{
return true;
}
}

93
webpack.config.js Normal file
View File

@ -0,0 +1,93 @@
const Path = require('path');
const dir = require('node-dir');
const webpackConfig = require('@silverstripe/webpack-config');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const {
resolveJS,
externalJS,
moduleJS,
pluginJS,
moduleCSS,
pluginCSS,
} = webpackConfig;
const ENV = process.env.NODE_ENV;
const PATHS = {
MODULES: 'node_modules',
MODULES_ABS: Path.resolve('node_modules'),
FILES_PATH: '../',
ROOT: Path.resolve(),
SRC: Path.resolve('client/src'),
DIST: Path.resolve('client/dist'),
DIST_JS: Path.resolve('client/dist/js'),
LEGACY_SRC: Path.resolve('client/src/legacy'),
};
const copyData = [
{
from: PATHS.MODULES + '/jquery/dist/jquery.min.js',
to: PATHS.DIST_JS
},
];
/**
* Builds a list of files matching the `*.min.js` pattern to copy from a source
* directory to a dist directory.
*/
const addMinFiles = (from, to) => {
const sourceDir = PATHS.MODULES_ABS + from;
dir.files(sourceDir, (err, files) => {
if (err) throw err;
files.forEach(file => {
filename = file.replace(sourceDir, '');
if (!filename.match(/\.min\.js$/)) {
return;
}
copyData.push({
from: PATHS.MODULES + from + filename,
to: PATHS.DIST_JS + to + filename
})
});
});
};
addMinFiles('/jquery-validation/dist', '/jquery-validation');
const config = [
{
name: 'js',
entry: {
CommentsInterface: `${PATHS.LEGACY_SRC}/CommentsInterface.js`,
},
output: {
path: PATHS.DIST,
filename: 'js/[name].js',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
resolve: resolveJS(ENV, PATHS),
externals: externalJS(ENV, PATHS),
module: moduleJS(ENV, PATHS),
plugins: pluginJS(ENV, PATHS).concat([
new CopyWebpackPlugin(copyData)
])
},
{
name: 'css',
entry: {
comments: `${PATHS.SRC}/styles/comments.scss`,
cms: `${PATHS.SRC}/styles/cms.scss`,
},
output: {
path: PATHS.DIST,
filename: 'styles/[name].css',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
module: moduleCSS(ENV, PATHS),
plugins: pluginCSS(ENV, PATHS),
},
];
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
module.exports = (process.env.WEBPACK_CHILD)
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
: module.exports = config;

10054
yarn.lock Normal file

File diff suppressed because it is too large Load Diff