Compare commits
399 Commits
Author | SHA1 | Date |
---|---|---|
Guy Sartorelli | 277f84392f | |
Steve Boyd | ca593443d1 | |
Guy Sartorelli | 67c42b363e | |
Guy Sartorelli | 7e202debb7 | |
dependabot[bot] | b988465bf1 | |
Guy Sartorelli | 4a9af98a70 | |
Steve Boyd | f5157aaf3c | |
Guy Sartorelli | dde8ac9bac | |
Maxime Rainville | dda2dcbfff | |
Guy Sartorelli | 7448c2050a | |
Maxime Rainville | 2fccdbe916 | |
Steve Boyd | 5729dc5c70 | |
Guy Sartorelli | 428b503a70 | |
Guy Sartorelli | 1dc37ac0c1 | |
Guy Sartorelli | 1ad761698b | |
Guy Sartorelli | e08e51d7f3 | |
Steve Boyd | 0c7b2e987b | |
Sabina Talipova | 5d044bd6eb | |
Steve Boyd | b771bb624a | |
Guy Sartorelli | 9bfef9e931 | |
Guy Sartorelli | 2b5bde3d8f | |
Steffen Maass | 57a9617d5b | |
Steve Boyd | c3ef4d6efc | |
Steve Boyd | a88bd6fff6 | |
Steve Boyd | 068f283833 | |
Guy Sartorelli | bffe690afd | |
Steve Boyd | 6081aeba8f | |
Guy Sartorelli | 6974bfd8ba | |
Steve Boyd | b9341d6185 | |
Steve Boyd | a3f2f3cd48 | |
Steve Boyd | 05a3628471 | |
Guy Sartorelli | bf6e56bd10 | |
Steve Boyd | ff6db7d480 | |
Will Rossiter | d87dbc8367 | |
Steve Boyd | 6782048463 | |
dependabot[bot] | a3479443df | |
dependabot[bot] | 9ef77e393c | |
Guy Sartorelli | 3dc22e9c39 | |
Steve Boyd | e7f9e60352 | |
github-actions | efe5b1ef6f | |
Maxime Rainville | 3d6e44aaa1 | |
Steve Boyd | 1d6eae193c | |
Daniel Hensby | 7284a920ed | |
Steve Boyd | 536d965d9c | |
Steve Boyd | 1aa560ff74 | |
Steve Boyd | 421e08cba5 | |
Steve Boyd | 91d2db1020 | |
Steve Boyd | 9258ce6fa4 | |
Maxime Rainville | 8a3b569653 | |
Steve Boyd | aa81f8cdd2 | |
Steve Boyd | 6895ac4f9d | |
Steve Boyd | a3c7943fdd | |
Steve Boyd | 43e221daa8 | |
Steve Boyd | 6d9a808cd4 | |
Maxime Rainville | 7eb8ebedf4 | |
Steve Boyd | 305ae6b4b8 | |
Loz Calver | 066b28bb3d | |
dependabot[bot] | 5cee54faca | |
dependabot[bot] | 4cfecdfcd6 | |
dependabot[bot] | 3a4130302d | |
dependabot[bot] | a4eeec4bba | |
Steve Boyd | 545df1d54f | |
Steve Boyd | 9c3c1e8120 | |
Steve Boyd | 38495c69f0 | |
Steve Boyd | e6971b82c6 | |
Steve Boyd | 362cb05029 | |
Steve Boyd | 5bda1a7076 | |
Garion Herman | 175664d5a8 | |
Steve Boyd | 3b37fdb1ae | |
Steve Boyd | fb15ba7d43 | |
Garion Herman | 06b243b1e4 | |
Steve Boyd | 668f90d2f9 | |
Maxime Rainville | 867d0e8281 | |
Steve Boyd | c065d0c5de | |
Steve Boyd | 739b211afb | |
Garion Herman | d12a71ddff | |
Steve Boyd | c834e19631 | |
dependabot[bot] | a8d1cf1355 | |
Garion Herman | f5f6e8f08f | |
dependabot[bot] | 8bc492bb77 | |
dependabot[bot] | 68ec979987 | |
dependabot[bot] | f91b13ad96 | |
dependabot[bot] | 6972da84a6 | |
dependabot[bot] | 99b2ed4dfc | |
dependabot[bot] | 1e9d8fcd1b | |
dependabot[bot] | bcbc58f46e | |
Steve Boyd | e88c7d5154 | |
Maxime Rainville | edfb9d7b74 | |
Maxime Rainville | 932ef5d284 | |
torleif | 82c817c452 | |
Robbie Averill | aaa9328a72 | |
dependabot[bot] | e2db114158 | |
Garion Herman | cfdffb394a | |
Garion Herman | d0b6181b48 | |
Andre Kiste | ce450103ac | |
Maxime Rainville | 05c5cb64a7 | |
Andre Kiste | 408845b01a | |
Maxime Rainville | 85374a1f76 | |
Dylan Wagstaff | 07b24073dd | |
dependabot[bot] | 250e522887 | |
Dylan Wagstaff | 9d9ab35cc6 | |
AdamSawoscianik | d5b9a2818c | |
Guy Marriott | e21a09234a | |
Jason Irish | fbfead9c9e | |
Will Rossiter | b3fe6d82b4 | |
Heath Dunlop | 617501efed | |
Robbie Averill | f4c82f238e | |
Jason Irish | c0f0c99e56 | |
Dylan Wagstaff | f8eeeee4b8 | |
Will Rossiter | cad9369f7b | |
Robbie Averill | c16ca56408 | |
Garion Herman | 67167c0809 | |
Robbie Averill | 8397bb5792 | |
Garion Herman | 2d0d949fc3 | |
Garion Herman | 14675b53bf | |
Robbie Averill | 9c4ec173fd | |
Heath Dunlop | 82845626bc | |
Robbie Averill | 780ea22b93 | |
Robbie Averill | f65f5b5697 | |
Guy Marriott | 74a317ab46 | |
Robbie Averill | 161f5f88bd | |
Robbie Averill | 67faa822f7 | |
Guy Marriott | aec3e2ba5b | |
Robbie Averill | 89204c0c1c | |
Robbie Averill | d8602d0e02 | |
Robbie Averill | 4d19e6c43e | |
Robbie Averill | 303bdef1c8 | |
Guy Marriott | 1358594070 | |
Dylan Wagstaff | 6fcc040485 | |
Dylan Wagstaff | 8b178b63ac | |
Garion Herman | 1e4378d212 | |
Robbie Averill | f0998faafb | |
Garion Herman | 74afde6d9b | |
Garion Herman | 9ff1db403e | |
Robbie Averill | ce1e934d57 | |
Peter Thaleikis | 005f58f1ee | |
Robbie Averill | 647c634dc4 | |
Guy Marriott | 7d60fc2fe2 | |
Guy Marriott | 18fbdfc94b | |
Robbie Averill | c4cfc44643 | |
Dylan Wagstaff | b6a2c608ac | |
Robbie Averill | d504d4a847 | |
Guy Marriott | f5c7024aaf | |
Dylan Wagstaff | 3812057b00 | |
Guy Marriott | 20c6a50f12 | |
Dylan Wagstaff | 0535d29f8d | |
Guy Marriott | 7691237fe5 | |
Guy Marriott | a57c83c121 | |
Dylan Wagstaff | dc1f8622e0 | |
Dylan Wagstaff | 4ee9720081 | |
Dylan Wagstaff | d7761290f8 | |
Robbie Averill | e101b74778 | |
Robbie Averill | 8c08f43376 | |
Dylan Wagstaff | f74d91041b | |
Robbie Averill | d38e7c5b67 | |
Robbie Averill | 97461f8f35 | |
Guy Marriott | def91bb6d1 | |
Robbie Averill | ec60328a0c | |
Robbie Averill | c0ca79090b | |
Robbie Averill | 9a57c3802c | |
Robbie Averill | 9f9dc67950 | |
Robbie Averill | 94950ee79c | |
Simon Gow | 747d4f4402 | |
Robbie Averill | cf86b2bb21 | |
Luke Edwards | 75d63209aa | |
Robbie Averill | 04a5257e40 | |
Robbie Averill | 19619b083e | |
micmania1 | c0a01dbc91 | |
Robbie Averill | 6d7b14367a | |
pjayme | 77f47af5cb | |
Dylan Wagstaff | a1a42009cc | |
Robbie Averill | c7235e1c5d | |
Robbie Averill | 4e23771d79 | |
Robbie Averill | 52b8ea9104 | |
Robbie Averill | 8bd79eac1f | |
Robbie Averill | f357ca6b7f | |
Robbie Averill | 886c5be21a | |
Robbie Averill | 9dab444f33 | |
Dylan Wagstaff | 2c00421cd2 | |
Robbie Averill | 32ec3bde50 | |
Dylan Wagstaff | be71d19d9e | |
Robbie Averill | 788cb6e6d1 | |
Robbie Averill | eca3ac0e94 | |
Robbie Averill | 01cc198a2d | |
Robbie Averill | 06acad661f | |
Robbie Averill | 42a15f7241 | |
Guy | c22daa2ee0 | |
Robbie Averill | 614b525c04 | |
Dylan Wagstaff | 02db1cc86e | |
Daniel Hensby | aa46291adf | |
Dennisprins93 | 386f8602f0 | |
Robbie Averill | eb0415a938 | |
Raissa North | a886f68c58 | |
Dylan | 276ddb0c86 | |
Dylan Wagstaff | cc46ccf89e | |
Robbie Averill | 3e0cae0cc9 | |
Robbie Averill | 4385299f1f | |
Cameron Grant | abb45aa733 | |
Robbie Averill | 532b49c537 | |
Raissa North | 8d5edc7821 | |
Robbie Averill | 90c42ff027 | |
Daniel Hensby | 909066c0a8 | |
Robbie Averill | 192ddbb4b5 | |
Daniel Hensby | b90ec7715e | |
Unknown | ae59e82021 | |
Robbie Averill | 17714f221a | |
Dylan Wagstaff | a25668eba0 | |
Dylan Wagstaff | 7b38707fde | |
Robbie Averill | 4bf0a83ad3 | |
Robbie Averill | b02928e02a | |
Robbie Averill | 4136bd7a63 | |
Robbie Averill | b68ff56624 | |
Robbie Averill | 4e2965e86b | |
Robbie Averill | 85c9981962 | |
Robbie Averill | 82c4a1b9be | |
Dylan Wagstaff | 3501ef7490 | |
Robbie Averill | 75d7de9aa6 | |
Robbie Averill | 216ad8eb54 | |
Daniel Hensby | 118de6b595 | |
Robbie Averill | 3f6f651eea | |
Robbie Averill | c6f8973342 | |
Dylan Wagstaff | 8919f6da2a | |
Shea Dawson | b1e9722320 | |
Will Rossiter | 8c43e097cd | |
Robbie Averill | 7700d88d4a | |
Robbie Averill | fa61608c7d | |
Will Rossiter | f294ab26d6 | |
Will Rossiter | 9366ef603e | |
Will Rossiter | d9333b3160 | |
Will Rossiter | 7fff17b317 | |
Will Rossiter | 73a981456f | |
Will Rossiter | 2fc7e7a004 | |
Will Rossiter | 0c964d1ed8 | |
Will Rossiter | 82c139e30a | |
Will Rossiter | 18761879cf | |
Will Rossiter | 4e2d22e16d | |
Franco Springveldt | 8e79a1368e | |
Will Rossiter | 85e8384151 | |
Daniel Hensby | fb3cc1b861 | |
Robbie Averill | 4df49c8db7 | |
Daniel Hensby | 06439dde1f | |
Daniel Hensby | d74bc59b98 | |
Daniel Hensby | f276983611 | |
Andrew Aitken-Fincham | e7c358f730 | |
Daniel Hensby | 8383ff1f81 | |
Daniel Hensby | bd5b007394 | |
Robbie Averill | 8ede35e6bd | |
Robbie Averill | c02d851500 | |
Rastislav Brandobur | 9912285761 | |
Robbie Averill | f27b357b06 | |
Rastislav Brandobur | 79e16d5661 | |
Paul | 2d076e4267 | |
Robbie Averill | 5a22f6faa5 | |
Daniel Hensby | 2ccd8d8af5 | |
Rastislav Brandobur | b3dac91090 | |
Daniel Hensby | 5181ac8be3 | |
Robbie Averill | f8d5b27d50 | |
Robbie Averill | fc4d78605a | |
Robbie Averill | d1697606e1 | |
Robbie Averill | d1b947afbe | |
Robbie Averill | 07586d7579 | |
Damian Mooyman | 19224d9813 | |
Damian Mooyman | 978879e01c | |
Robbie Averill | d4e58316e5 | |
Andrew Aitken-Fincham | 279d47f43c | |
Robbie Averill | 7a92b0915d | |
Robbie Averill | 6aeab9f823 | |
Robbie Averill | ab3f35257b | |
Robbie Averill | c698632aae | |
Damian Mooyman | f74b13a2b7 | |
Damian Mooyman | 86e6d05bf5 | |
Daniel Hensby | 2b8ff849ed | |
Will Rossiter | 578845bd19 | |
Stéphane Guevremont | 9cb935203a | |
Stéphane Guevremont | b5557a2a51 | |
Damian Mooyman | 09db00a783 | |
Daniel Hensby | 3541dd6002 | |
Janne Klouman | b34a41e828 | |
Janne Klouman | 821a6dc383 | |
Janne Klouman | 6822ecd669 | |
Janne Klouman | 801025c39a | |
Damian Mooyman | 1a94d1cd55 | |
Christopher Pitt | a365495226 | |
Damian Mooyman | 93ddc3cc08 | |
Damian Mooyman | 55d0bd62ce | |
Damian Mooyman | ebc1728bf8 | |
Damian Mooyman | 7cbabe33ad | |
Daniel Hensby | 2ce4d2a5f7 | |
Damian Mooyman | ad78ac176c | |
Damian Mooyman | 9f527ef54d | |
Fred Condo | 39cc3101a6 | |
Daniel Hensby | bb0de16f36 | |
Gordon Anderson | 3610414ed0 | |
Daniel Hensby | f187a02a72 | |
Damian Mooyman | e42a4c5c81 | |
Damian Mooyman | 02d5485f18 | |
helpfulrobot | b854dab635 | |
scott1702 | 5d99054805 | |
Daniel Hensby | af60346dfe | |
Damian Mooyman | 4b31713128 | |
Damian Mooyman | c2fee6bd33 | |
Damian Mooyman | b1bf62d49c | |
Gordon Anderson | 152137d475 | |
Gordon Anderson | 8ef9183457 | |
Gordon Anderson | fd153265e9 | |
Damian Mooyman | 7721c20e80 | |
Damian Mooyman | aaa95e0c4c | |
Gordon Anderson | 551841fbda | |
Gordon Anderson | a3d71cb1b7 | |
Damian Mooyman | 5ec6724393 | |
Damian Mooyman | 96f93c0e0e | |
Damian Mooyman | 788b6fa247 | |
Gordon Anderson | 7152fbc235 | |
Gordon Anderson | c730a731f3 | |
Gordon Anderson | 41bc663d5d | |
Damian Mooyman | 384979662a | |
Damian Mooyman | 7d07572994 | |
Daniel Hensby | 1d63b8d323 | |
Daniel Hensby | 1243d0d848 | |
Gordon Anderson | 43a2414f50 | |
Damian Mooyman | 0e64cc4fe0 | |
Gordon Anderson | 450ed93f36 | |
Gordon Anderson | 8dc36df7a4 | |
Gordon Anderson | eba88fe869 | |
Damian Mooyman | a177b49c35 | |
Gordon Anderson | 277a917bf5 | |
Damian Mooyman | 72a7899e3d | |
helpfulrobot | ca22a337b3 | |
Daniel Hensby | 167e4dd02a | |
helpfulrobot | f40793723b | |
Daniel Hensby | d0a4aa9bfb | |
Daniel Hensby | d403e0437e | |
Daniel Hensby | 8ee4caaef6 | |
Daniel Hensby | 061064468c | |
Daniel Hensby | dc12833404 | |
helpfulrobot | 64170cfcff | |
helpfulrobot | a29591deb0 | |
helpfulrobot | 26f731573f | |
Scott Hutchinson | 04b95cb943 | |
helpfulrobot | 0a72258149 | |
helpfulrobot | 0ebdc10f02 | |
Damian Mooyman | 435618a07d | |
Damian Mooyman | ed1030194f | |
Damian Mooyman | e8ba40f42c | |
Christopher Pitt | 671766364b | |
Christopher Pitt | 4d538f565c | |
Damian Mooyman | e26e4d916b | |
Will Rossiter | ac92dbb515 | |
Damian Mooyman | 018881c345 | |
Christopher Pitt | c6c4702caf | |
Scott Hutchinson | 877748b25a | |
Damian Mooyman | b989aab588 | |
Christopher Pitt | 31dd95a76d | |
Damian Mooyman | 752d509d1e | |
Damian Mooyman | d3a9e78002 | |
Daniel Hensby | 9d0a4ebb5e | |
Christopher Pitt | bc8f2c1ad7 | |
Damian Mooyman | 74c14a81c7 | |
Christopher Pitt | 1432fe1cab | |
Damian Mooyman | 9ee9c5bdca | |
Christopher Pitt | 3e4786c312 | |
Damian Mooyman | 832fc8685a | |
Christopher Pitt | a31fc98b38 | |
Damian Mooyman | 297fb8666d | |
Gordon Anderson | 563a617b14 | |
Damian Mooyman | 2aa8d974b0 | |
Christopher Pitt | 304dee2792 | |
Damian Mooyman | d0cd57104d | |
Damian Mooyman | 651a4e4977 | |
Daniel Caine | e8e5abdc23 | |
Christopher Pitt | 8d987e3336 | |
Damian Mooyman | f772d32d55 | |
Christopher Pitt | 24aa0f938f | |
Damian Mooyman | 138bbbfd84 | |
Damian Mooyman | ad93c5c3ab | |
Damian Mooyman | 298ac89ad6 | |
scott1702 | a5bed7f27f | |
Christopher Pitt | 614da962ce | |
Damian Mooyman | a0974dd399 | |
Damian Mooyman | 468df23801 | |
Christopher Pitt | adabc623f6 | |
Damian Mooyman | 75be840dce | |
Christopher Pitt | 5c71b2f141 | |
Christopher Pitt | 0c698a813a | |
Damian Mooyman | 85653fea1e | |
Damian Mooyman | 26361cccba | |
Christopher Pitt | 086dd03b5c | |
Christopher Pitt | 1410702fce | |
Christopher Pitt | a004dfe780 | |
Christopher Pitt | 7585b5d19f | |
Damian Mooyman | 3deea94a8c | |
Damian Mooyman | eadde16232 | |
Christopher Pitt | 162ad33e26 | |
Christopher Pitt | 128fa67f65 | |
Damian Mooyman | 69c928650a | |
Christopher Pitt | 5254d72ccd | |
Damian Mooyman | 6ebbd9e1ac | |
Sam Minnée | f9c93ef9aa | |
Damian Mooyman | 1900ab14b5 |
|
@ -6,19 +6,21 @@
|
|||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
[*.{yml,js,json,css,scss,feature}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[{.travis.yml,package.json}]
|
||||
# The indent size used in the `package.json` file cannot be changed
|
||||
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
|
||||
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
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# Ignore dist files
|
||||
client/dist/
|
||||
|
||||
# Ignore legacy files
|
||||
client/src/legacy/
|
||||
|
||||
# Ignore auto-generated language files
|
||||
client/lang/
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('@silverstripe/eslint-config/.eslintrc');
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
.sass-cache
|
||||
/.php_cs.cache
|
||||
/node_modules/
|
23
.travis.yml
23
.travis.yml
|
@ -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=3
|
||||
- DB=PGSQL CORE_RELEASE=3.1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.4
|
||||
env: DB=MYSQL CORE_RELEASE=3
|
||||
|
||||
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:
|
||||
- vendor/bin/phpunit comments/tests/
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
17
LICENSE
|
@ -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.
|
26
README.md
26
README.md
|
@ -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.
|
||||
|
|
31
_config.php
31
_config.php
|
@ -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(Config::inst()->get('Commenting', 'sitetree_comments') && class_exists('SiteTree') && !Commenting::has_commenting('SiteTree')) {
|
||||
Commenting::add('SiteTree');
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
SilverStripe\ORM\DatabaseAdmin:
|
||||
classname_value_remapping:
|
||||
Comment: SilverStripe\Comments\Model\Comment
|
|
@ -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
|
||||
|
|
|
@ -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}});
|
|
@ -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(/ | /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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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ämä kenttä on pakollinen.",email:"Syötä oikea sähköpostiosoite.",url:"Syötä oikea URL-osoite.",date:"Syötä oikea päivämäärä.",dateISO:"Syötä oikea päivämäärä muodossa VVVV-KK-PP.",number:"Syötä luku.",creditcard:"Syötä voimassa oleva luottokorttinumero.",digits:"Syötä pelkästään numeroita.",equalTo:"Syötä sama arvo uudestaan.",maxlength:a.validator.format("Voit syöttää enintään {0} merkkiä."),minlength:a.validator.format("Vähintään {0} merkkiä."),rangelength:a.validator.format("Syötä vähintään {0} ja enintään {1} merkkiä."),range:a.validator.format("Syötä arvo väliltä {0}–{1}."),max:a.validator.format("Syötä arvo, joka on enintään {0}."),min:a.validator.format("Syötä arvo, joka on vähintään {0}.")}),a});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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'estensione valida",maxlength:a.validator.format("Non inserire più 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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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ørre eller lik {0}."),step:a.validator.format("Angi en verdi ganger {0}."),creditcard:"Angi et gyldig kredittkortnummer."}),a});
|
|
@ -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});
|
|
@ -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 é requerido.",remote:"Por favor, corrija este campo.",email:"Por favor, forneça um endereço de email válido.",url:"Por favor, forneça uma URL válida.",date:"Por favor, forneça uma data válida.",dateISO:"Por favor, forneça uma data válida (ISO).",number:"Por favor, forneça um número válido.",digits:"Por favor, forneça somente dígitos.",creditcard:"Por favor, forneça um cartão de crédito válido.",equalTo:"Por favor, forneça o mesmo valor novamente.",maxlength:a.validator.format("Por favor, forneça não mais que {0} caracteres."),minlength:a.validator.format("Por favor, forneça ao menos {0} caracteres."),rangelength:a.validator.format("Por favor, forneça um valor entre {0} e {1} caracteres de comprimento."),range:a.validator.format("Por favor, forneça um valor entre {0} e {1}."),max:a.validator.format("Por favor, forneça um valor menor ou igual a {0}."),min:a.validator.format("Por favor, forneça um valor maior ou igual a {0}."),step:a.validator.format("Por favor, forneça um valor múltiplo de {0}."),maxWords:a.validator.format("Por favor, forneça com {0} palavras ou menos."),minWords:a.validator.format("Por favor, forneça pelo menos {0} palavras."),rangeWords:a.validator.format("Por favor, forneça entre {0} e {1} palavras."),accept:"Por favor, forneça um tipo válido.",alphanumeric:"Por favor, forneça somente com letras, números e sublinhados.",bankaccountNL:"Por favor, forneça com um número de conta bancária válida.",bankorgiroaccountNL:"Por favor, forneça um banco válido ou número de conta.",bic:"Por favor, forneça um código BIC válido.",cifES:"Por favor, forneça um código CIF válido.",creditcardtypes:"Por favor, forneça um número de cartão de crédito válido.",currency:"Por favor, forneça uma moeda válida.",dateFA:"Por favor, forneça uma data correta.",dateITA:"Por favor, forneça uma data correta.",dateNL:"Por favor, forneça uma data correta.",extension:"Por favor, forneça um valor com uma extensão válida.",giroaccountNL:"Por favor, forneça um número de conta corrente válido.",iban:"Por favor, forneça um código IBAN válido.",integer:"Por favor, forneça um número não decimal.",ipv4:"Por favor, forneça um IPv4 válido.",ipv6:"Por favor, forneça um IPv6 válido.",lettersonly:"Por favor, forneça apenas com letras.",letterswithbasicpunc:"Por favor, forneça apenas letras ou pontuações.",mobileNL:"Por favor, forneceça um número válido de telefone.",mobileUK:"Por favor, forneceça um número válido de telefone.",nieES:"Por favor, forneça um NIE válido.",nifES:"Por favor, forneça um NIF válido.",nowhitespace:"Por favor, não utilize espaços em branco.",pattern:"O formato fornecido é inválido.",phoneNL:"Por favor, forneça um número de telefone válido.",phoneUK:"Por favor, forneça um número de telefone válido.",phoneUS:"Por favor, forneça um número de telefone válido.",phonesUK:"Por favor, forneça um número de telefone válido.",postalCodeCA:"Por favor, forneça um número de código postal válido.",postalcodeIT:"Por favor, forneça um número de código postal válido.",postalcodeNL:"Por favor, forneça um número de código postal válido.",postcodeUK:"Por favor, forneça um número de código postal válido.",postalcodeBR:"Por favor, forneça um CEP válido.",require_from_group:a.validator.format("Por favor, forneç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ça um estado válido.",strippedminlength:a.validator.format("Por favor, forneça pelo menos {0} caracteres."),time:"Por favor, forneça um horário válido, no intervado de 00:00 a 23:59.",time12h:"Por favor, forneça um horário válido, no intervado de 01:00 a 12:59 am/pm.",url2:"Por favor, forneça uma URL válida.",vinUS:"O número de identificação de veículo informado (VIN) é inválido.",zipcodeUS:"Por favor, forneça um código postal americano válido.",ziprange:"O código postal deve estar entre 902xx-xxxx e 905xx-xxxx",cpfBR:"Por favor, forneça um CPF válido.",nisBR:"Por favor, forneça um NIS/PIS válido",cnhBR:"Por favor, forneça um CNH válido.",cnpjBR:"Por favor, forneça um CNPJ válido."}),a});
|
|
@ -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ório.",remote:"Por favor, corrija este campo.",email:"Por favor, introduza um endereço eletrónico válido.",url:"Por favor, introduza um URL válido.",date:"Por favor, introduza uma data válida.",dateISO:"Por favor, introduza uma data válida (ISO).",number:"Por favor, introduza um número válido.",digits:"Por favor, introduza apenas dígitos.",creditcard:"Por favor, introduza um número de cartão de crédito válido.",equalTo:"Por favor, introduza de novo o mesmo valor.",extension:"Por favor, introduza um ficheiro com uma extensão válida.",maxlength:a.validator.format("Por favor, nã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álido.",nieES:"Por favor, introduza um NIE válido.",cifES:"Por favor, introduza um CIF válido."}),a});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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ält är obligatoriskt.",remote:"Var snäll och åtgärda detta fält.",maxlength:a.validator.format("Du får ange högst {0} tecken."),minlength:a.validator.format("Du må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 (ÅÅÅÅ-MM-DD).",number:"Ange ett korrekt nummer.",digits:"Ange endast siffror.",equalTo:"Ange samma värde igen.",range:a.validator.format("Ange ett värde mellan {0} och {1}."),max:a.validator.format("Ange ett värde som är mindre eller lika med {0}."),min:a.validator.format("Ange ett värde som är större eller lika med {0}."),creditcard:"Ange ett korrekt kreditkortsnummer.",pattern:"Ogiltigt format."}),a});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
|
@ -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});
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.cms table.ss-gridfield-table tr.spam{background-color:#ffd8d8}.cms table.ss-gridfield-table tr.spam:hover{background-color:#fff2f2}
|
|
@ -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}
|
|
@ -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",
|
||||
});
|
||||
}
|
|
@ -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",
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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).
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,188 +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();
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var bool Whether to enable commenting on SiteTree objects by default
|
||||
*/
|
||||
private static $sitetree_comments = true;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,484 +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 = "";
|
||||
protected $fallbackReturnURL = null;
|
||||
|
||||
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() {
|
||||
$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->IsSpam = true;
|
||||
$comment->Moderated = true;
|
||||
$comment->write();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->IsSpam = false;
|
||||
$comment->Moderated = true;
|
||||
$comment->write();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->IsSpam = false;
|
||||
$comment->Moderated = true;
|
||||
$comment->write();
|
||||
|
||||
return $this->request->isAjax()
|
||||
? $comment->renderWith('CommentsInterface_singlecomment')
|
||||
: $this->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$this->fallbackReturnURL = $comment->Link();
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
if(!Commenting::can_member_post($class)) {
|
||||
return Security::permissionFailure(
|
||||
$this,
|
||||
_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."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if($member = Member::currentUser()) {
|
||||
$form->Fields()->push(new HiddenField("AuthorID", "Author ID", $member->ID));
|
||||
}
|
||||
|
||||
// is moderation turned on
|
||||
$requireModeration = Commenting::get_config_value($class, 'require_moderation');
|
||||
if(!$requireModeration){
|
||||
$requireModerationNonmembers = Commenting::get_config_value($class, 'require_moderation_nonmembers');
|
||||
$requireModeration = $requireModerationNonmembers ? !Member::currentUser() : false;
|
||||
}
|
||||
|
||||
// we want to show a notification if comments are moderated
|
||||
if ($requireModeration) {
|
||||
Session::set('CommentsModerated', 1);
|
||||
}
|
||||
|
||||
|
||||
$comment = new Comment();
|
||||
$form->saveInto($comment);
|
||||
|
||||
$comment->AllowHtml = Commenting::get_config_value($class, 'html_allowed');
|
||||
$comment->Moderated = !$requireModeration;
|
||||
|
||||
// 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);
|
||||
|
||||
// Find parent link
|
||||
if(!empty($data['ReturnURL'])) {
|
||||
$url = $data['ReturnURL'];
|
||||
} elseif($parent = $comment->getParent()) {
|
||||
$url = $parent->Link();
|
||||
} else {
|
||||
return $this->redirectBack();
|
||||
}
|
||||
|
||||
// Given a redirect page exists, attempt to link to the correct anchor
|
||||
if(!$comment->Moderated) {
|
||||
// Display the "awaiting moderation" text
|
||||
$holder = Commenting::get_config_value($comment->BaseClass, 'comments_holder_id');
|
||||
$hash = "{$holder}_PostCommentForm_error";
|
||||
} elseif($comment->IsSpam) {
|
||||
// Link to the form with the error message contained
|
||||
$hash = $form->FormName();
|
||||
} else {
|
||||
// Link to the moderated, non-spam comment
|
||||
$hash = $comment->Permalink();
|
||||
}
|
||||
|
||||
return $this->redirect(Controller::join_links($url, "#{$hash}"));
|
||||
}
|
||||
|
||||
public function doPreviewComment($data, $form) {
|
||||
$data['IsPreview'] = 1;
|
||||
|
||||
return $this->doPostComment($data, $form);
|
||||
}
|
||||
|
||||
public function redirectBack() {
|
||||
// Don't cache the redirect back ever
|
||||
HTTP::set_cache_age(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');
|
||||
} else if($this->request->isAjax() && $this->request->getHeader('X-Backurl')) {
|
||||
$url = $this->request->getHeader('X-Backurl');
|
||||
} else if($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);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,596 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @method Member Author()
|
||||
* @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",
|
||||
"SecretToken" => "Varchar(255)",
|
||||
);
|
||||
|
||||
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',
|
||||
'DeleteLink' => 'Varchar',
|
||||
'SpamLink' => 'Varchar',
|
||||
'HamLink' => 'Varchar',
|
||||
'ApproveLink' => 'Varchar',
|
||||
'Permalink' => '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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Comment_SecurityToken
|
||||
*/
|
||||
public function getSecurityToken() {
|
||||
return Injector::inst()->createWithArgs('Comment_SecurityToken', array($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = Member::currentUser();
|
||||
if(!$member) return false;
|
||||
|
||||
$url = Controller::join_links(
|
||||
Director::baseURL(),
|
||||
"CommentingController",
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
$hidden = array('ParentID', 'AuthorID', 'BaseClass', 'AllowHtml', 'SecretToken');
|
||||
|
||||
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);
|
||||
$config->set('Cache.SerializerPath', getTempFolder());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to generate cryptographically secure tokens for comment moderation
|
||||
*/
|
||||
class Comment_SecurityToken {
|
||||
|
||||
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 function_exists('hash_pbkdf2')
|
||||
? hash_pbkdf2('sha256', $this->secret, $salt, 1000, 30)
|
||||
: $this->hash_pbkdf2('sha256', $this->secret, $salt, 100, 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 type $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 = Member::currentUser();
|
||||
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;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* PBKDF2 Implementation (described in RFC 2898) from php.net
|
||||
*-----------------------------------------------------------
|
||||
* @param string a hash algorithm
|
||||
* @param string p password
|
||||
* @param string s salt
|
||||
* @param int c iteration count (use 1000 or higher)
|
||||
* @param int kl derived key length
|
||||
* @param int st start position of result
|
||||
*
|
||||
* @return string derived key
|
||||
*/
|
||||
private function hash_pbkdf2 ($a, $p, $s, $c, $kl, $st=0) {
|
||||
|
||||
$kb = $st+$kl; // Key blocks to compute
|
||||
$dk = ''; // Derived key
|
||||
|
||||
// Create key
|
||||
for ($block=1; $block<=$kb; $block++) {
|
||||
|
||||
// Initial hash for this block
|
||||
$ib = $h = hash_hmac($a, $s . pack('N', $block), $p, true);
|
||||
|
||||
// Perform block iterations
|
||||
for ($i=1; $i<$c; $i++) {
|
||||
// XOR each iterate
|
||||
$ib ^= ($h = hash_hmac($a, $h, $p, true));
|
||||
}
|
||||
|
||||
$dk .= $ib; // Append iterated block
|
||||
|
||||
}
|
||||
|
||||
// Return derived key of correct length
|
||||
return substr($dk, $st, $kl);
|
||||
}
|
||||
}
|
|
@ -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 = CommentingController::create();
|
||||
$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();
|
||||
}
|
||||
}
|
|
@ -1,25 +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.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ezyang/htmlpurifier": "4.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~3.7@stable"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
"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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/** Deprecated. Remove in 4.0 release. */
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
|
@ -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: الرابط
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
121
lang/de.yml
121
lang/de.yml
|
@ -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
|
||||
|
|
|
@ -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
|
116
lang/en.yml
116
lang/en.yml
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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: 'نشانی اینترنتی'
|
121
lang/fi.yml
121
lang/fi.yml
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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: 'כתובת הדף'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)'
|
|
@ -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
|
|
@ -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
|
|
@ -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: コメント
|
|
@ -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
|
104
lang/lt.yml
104
lang/lt.yml
|
@ -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'
|
||||
|
|
|
@ -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
|
66
lang/nb.yml
66
lang/nb.yml
|
@ -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: på
|
||||
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
|
||||
|
|
83
lang/nl.yml
83
lang/nl.yml
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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: ความคิดเห็น
|
|
@ -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
|
|
@ -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: 网址
|
|
@ -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: 網址
|
|
@ -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.
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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.')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}"));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -1,73 +1,53 @@
|
|||
<% 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 $canPostComment %>
|
||||
<% 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>
|
||||
<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">« <% _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') %> »</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 %>
|
||||
<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 %>
|
||||
|
||||
|
|
|
@ -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 %>
|
|
@ -0,0 +1,21 @@
|
|||
<% if $MoreThanOnePage %>
|
||||
<div class="comments-pagination">
|
||||
<p>
|
||||
<% if $PrevLink %>
|
||||
<a href="$PrevLink.ATT" class="previous">« <% _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') %> »</a>
|
||||
<% end_if %>
|
||||
</p>
|
||||
</div>
|
||||
<% end_if %>
|
|
@ -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 %>
|
|
@ -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>
|
|
@ -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 %>
|
|
@ -0,0 +1 @@
|
|||
<% include CommentPagination %>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,382 +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() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
|
||||
$delete = $this->get('CommentingController/delete/'.$comment->ID.'?ajax=1');
|
||||
$this->assertEquals(403, $delete->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertTrue($check && $check->exists());
|
||||
Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => false]);
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
|
||||
// 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());
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->DeleteLink();
|
||||
$this->assertContains('CommentingController/delete/'.$commentID.'?t=', $adminComment1Link);
|
||||
$this->logInWithPermission('ADMIN');
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
// 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', $commentID);
|
||||
$this->assertTrue($check && $check->exists());
|
||||
// With spam comments set to display in frontend
|
||||
Config::modify()->merge(CommentableItem::class, 'comments', [
|
||||
'require_moderation' => true,
|
||||
'frontend_moderation' => false,
|
||||
'frontend_spam' => true,
|
||||
]);
|
||||
|
||||
// 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', $commentID);
|
||||
$this->assertFalse($check && $check->exists());
|
||||
}
|
||||
$this->logOut();
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
public function testSpamComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
$comment = $this->objFromFixture('Comment', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
|
||||
$spam = $this->get('CommentingController/spam/'.$comment->ID.'?ajax=1');
|
||||
$this->assertEquals(403, $spam->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
|
||||
$this->logInWithPermission('ADMIN');
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
|
||||
// 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', 'firstComA');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->SpamLink();
|
||||
$this->assertContains('CommentingController/spam/'.$commentID.'?t=', $adminComment1Link);
|
||||
// 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,
|
||||
]);
|
||||
|
||||
// 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', $comment->ID);
|
||||
$this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
|
||||
$this->logOut();
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
// 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', $commentID);
|
||||
$this->assertEquals(1, $check->IsSpam);
|
||||
$this->logInWithPermission('ADMIN');
|
||||
$this->assertEquals(4, $item->Comments()->Count());
|
||||
}
|
||||
|
||||
// Cannot re-spam spammed comment
|
||||
$this->assertNull($check->SpamLink());
|
||||
}
|
||||
/**
|
||||
* Test moderation options configured via the CMS
|
||||
*/
|
||||
public function testCommentCMSModerationList()
|
||||
{
|
||||
Config::modify()->merge(CommentableItem::class, 'comments', [
|
||||
'require_moderation' => true,
|
||||
'require_moderation_cms' => true,
|
||||
]);
|
||||
|
||||
public function testHamComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
$comment = $this->objFromFixture('Comment', 'secondComC');
|
||||
$commentID = $comment->ID;
|
||||
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
|
||||
$ham = $this->get('CommentingController/ham/'.$comment->ID.'?ajax=1');
|
||||
$this->assertEquals(403, $ham->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
|
||||
$item = $this->objFromFixture(CommentableItem::class, 'spammed');
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
$this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
|
||||
$this->assertEquals('None', $item->getModerationRequired());
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'secondComC');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->HamLink();
|
||||
$this->assertContains('CommentingController/ham/'.$commentID.'?t=', $adminComment1Link);
|
||||
$this->assertListEquals([
|
||||
['Name' => 'Comment 1'],
|
||||
['Name' => 'Comment 3']
|
||||
], $item->Comments(), 'Only 2 non spam posts should be shown');
|
||||
|
||||
// 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', $comment->ID);
|
||||
$this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
|
||||
// when moderated, only moderated, non spam posts should be shown.
|
||||
$item->ModerationRequired = 'NonMembersOnly';
|
||||
$item->write();
|
||||
|
||||
// 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', $commentID);
|
||||
$this->assertEquals(0, $check->IsSpam);
|
||||
$this->assertEquals('NonMembersOnly', $item->getModerationRequired());
|
||||
|
||||
// Cannot re-ham hammed comment
|
||||
$this->assertNull($check->HamLink());
|
||||
}
|
||||
|
||||
public function testApproveComment() {
|
||||
// Test anonymous user
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
$comment = $this->objFromFixture('Comment', 'secondComB');
|
||||
$commentID = $comment->ID;
|
||||
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
|
||||
$approve = $this->get('CommentingController/approve/'.$comment->ID.'?ajax=1');
|
||||
$this->assertEquals(403, $approve->getStatusCode());
|
||||
$check = DataObject::get_by_id('Comment', $commentID);
|
||||
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
|
||||
// Check that require_moderation overrides this option
|
||||
$item->ModerationRequired = 'Required';
|
||||
$item->write();
|
||||
$this->assertEquals('Required', $item->getModerationRequired());
|
||||
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
$this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
|
||||
$this->assertListEquals([
|
||||
['Name' => 'Comment 3']
|
||||
], $item->Comments(), 'Only 1 non spam, moderated post should be shown');
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
// Test authenticated user
|
||||
$this->logInAs('commentadmin');
|
||||
$comment = $this->objFromFixture('Comment', 'secondComB');
|
||||
$commentID = $comment->ID;
|
||||
$adminComment1Link = $comment->ApproveLink();
|
||||
$this->assertContains('CommentingController/approve/'.$commentID.'?t=', $adminComment1Link);
|
||||
// require_moderation_nonmembers still filters out unmoderated comments
|
||||
$item->ModerationRequired = 'NonMembersOnly';
|
||||
$item->write();
|
||||
$this->assertEquals(1, $item->Comments()->Count());
|
||||
|
||||
// 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', $comment->ID);
|
||||
$this->assertEquals(0, $check->Moderated, 'No permission to approve');
|
||||
$item->ModerationRequired = 'None';
|
||||
$item->write();
|
||||
$this->assertEquals(2, $item->Comments()->Count());
|
||||
}
|
||||
|
||||
// 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', $commentID);
|
||||
$this->assertEquals(1, $check->Moderated);
|
||||
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');
|
||||
|
||||
// Cannot re-approve approved comment
|
||||
$this->assertNull($check->ApproveLink());
|
||||
}
|
||||
// Test restriction free commenting
|
||||
$this->logOut();
|
||||
$this->assertFalse($item->CommentsRequireLogin);
|
||||
$this->assertTrue($item->canPostComment());
|
||||
|
||||
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';
|
||||
// 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());
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
// Test require login to post, but not any permissions
|
||||
Config::modify()->merge(CommentableItem::class, 'comments', [
|
||||
'required_permission' => false,
|
||||
]);
|
||||
$this->assertTrue($item->CommentsRequireLogin);
|
||||
|
||||
public function testSanitizesWithAllowHtml() {
|
||||
if(!class_exists('HTMLPurifier')) {
|
||||
$this->markTestSkipped('HTMLPurifier class not found');
|
||||
return;
|
||||
}
|
||||
$this->logOut();
|
||||
$this->assertFalse($item->canPostComment());
|
||||
$this->logInWithPermission('ANY_PERMISSION');
|
||||
$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 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);
|
||||
|
||||
// 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'
|
||||
);
|
||||
$this->logOut();
|
||||
$this->assertTrue($item->canPostComment());
|
||||
$this->assertFalse($item2->canPostComment());
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
|
||||
}
|
||||
// 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());
|
||||
|
||||
public function testDefaultTemplateRendersHtmlWithAllowHtml() {
|
||||
if(!class_exists('HTMLPurifier')) {
|
||||
$this->markTestSkipped('HTMLPurifier class not found');
|
||||
}
|
||||
// Test non-authenticated user
|
||||
$this->logInAs('visitor');
|
||||
$this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
|
||||
|
||||
$origAllowed = Commenting::get_config_value('CommentableItem', 'html_allowed');
|
||||
$item = new CommentableItem();
|
||||
$item->write();
|
||||
// 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);
|
||||
|
||||
// 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(
|
||||
'<p>my comment</p>',
|
||||
$html
|
||||
);
|
||||
// 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());
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', true);
|
||||
$html = $item->customise(array('CommentsEnabled' => true))->renderWith('CommentsInterface');
|
||||
$this->assertContains(
|
||||
'<p>my comment</p>',
|
||||
$html
|
||||
);
|
||||
// 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());
|
||||
}
|
||||
|
||||
Commenting::set_config_value('CommentableItem','html_allowed', $origAllowed);
|
||||
}
|
||||
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');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 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(
|
||||
'<p>my comment</p>',
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Member:
|
||||
SilverStripe\Security\Member:
|
||||
commentadmin:
|
||||
FirstName: admin
|
||||
commentadmin2:
|
||||
|
@ -6,23 +6,25 @@ Member:
|
|||
visitor:
|
||||
FirstName: visitor
|
||||
|
||||
Group:
|
||||
SilverStripe\Security\Group:
|
||||
commentadmins:
|
||||
Title: Admin
|
||||
Members: =>Member.commentadmin, =>Member.commentadmin2
|
||||
Members: =>SilverStripe\Security\Member.commentadmin, =>SilverStripe\Security\Member.commentadmin2
|
||||
|
||||
Permission:
|
||||
SilverStripe\Security\Permission:
|
||||
admin:
|
||||
Code: CMS_ACCESS_CommentAdmin
|
||||
Group: =>Group.commentadmins
|
||||
Group: =>SilverStripe\Security\Group.commentadmins
|
||||
|
||||
CommentableItem:
|
||||
SilverStripe\Comments\Tests\Stubs\CommentableItem:
|
||||
first:
|
||||
Title: First
|
||||
ProvideComments: 1
|
||||
CommentsRequireLogin: 0
|
||||
second:
|
||||
Title: Second
|
||||
ProvideComments: 1
|
||||
CommentsRequireLogin: 1
|
||||
third:
|
||||
Title: Third
|
||||
ProvideComments: 1
|
||||
|
@ -32,138 +34,159 @@ CommentableItem:
|
|||
spammed:
|
||||
ProvideComments: 1
|
||||
Title: spammed
|
||||
ModerationRequired: 'None'
|
||||
moderated:
|
||||
ProvideComments: 1
|
||||
Title: Moderate everything
|
||||
ModerationRequired: Required
|
||||
CommentsRequireLogin: 0
|
||||
|
||||
Comment:
|
||||
SilverStripe\Comments\Model\Comment:
|
||||
firstComA:
|
||||
ParentID: =>CommentableItem.first
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.first
|
||||
Name: FA
|
||||
Comment: textFA
|
||||
BaseClass: CommentableItem
|
||||
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:
|
||||
ParentID: =>CommentableItem.second
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
|
||||
Name: SA
|
||||
Comment: textSA
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
secondComB:
|
||||
ParentID: =>CommentableItem.second
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
|
||||
Name: SB
|
||||
Comment: textSB
|
||||
Moderated: 0
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
secondComC:
|
||||
ParentID: =>CommentableItem.second
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.second
|
||||
Name: SB
|
||||
Comment: textSB
|
||||
Moderated: 1
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
thirdComA:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TA
|
||||
Comment: textTA
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComB:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TB
|
||||
Comment: textTB
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComC:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComD:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
BaseClass: CommentableItem
|
||||
thirdComE:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
BaseClass: CommentableItem
|
||||
thirdComF:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComG:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComH:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComI:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComJ:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
thirdComK:
|
||||
ParentID: =>CommentableItem.third
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.third
|
||||
Name: TC
|
||||
Comment: textTC
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
disabledCom:
|
||||
ParentID: =>CommentableItem.nocomments
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.nocomments
|
||||
Name: Disabled
|
||||
Moderated: 0
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
testCommentList1:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
|
||||
Name: Comment 1
|
||||
Moderated: 0
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
testCommentList2:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
|
||||
Name: Comment 2
|
||||
Moderated: 1
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
testCommentList3:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
|
||||
Name: Comment 3
|
||||
Moderated: 1
|
||||
IsSpam: 0
|
||||
BaseClass: CommentableItem
|
||||
testCommentList4:
|
||||
ParentID: =>CommentableItem.spammed
|
||||
Parent: =>SilverStripe\Comments\Tests\Stubs\CommentableItem.spammed
|
||||
Name: Comment 4
|
||||
Moderated: 0
|
||||
IsSpam: 1
|
||||
BaseClass: CommentableItem
|
||||
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
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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,
|
||||
];
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
Loading…
Reference in New Issue