mirror of
https://github.com/silverstripe/silverstripe-contentreview
synced 2024-10-22 17:05:47 +02:00
Merge pull request #64 from creative-commoners/pulls/4.0/review-modal
API Add React modal popup for reviewing content in SiteTree
This commit is contained in:
commit
d00f37df76
@ -6,4 +6,4 @@ checks:
|
|||||||
duplication: true
|
duplication: true
|
||||||
|
|
||||||
filter:
|
filter:
|
||||||
paths: [code/*, tests/*]
|
paths: [src/*, tests/*]
|
||||||
|
20
.travis.yml
20
.travis.yml
@ -1,10 +1,15 @@
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
dist: trusty
|
dist: precise
|
||||||
|
|
||||||
|
addons:
|
||||||
|
firefox: "31.0"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- COMPOSER_ROOT_VERSION=4.0.x-dev
|
- COMPOSER_ROOT_VERSION=4.0.x-dev
|
||||||
|
- DISPLAY=":99"
|
||||||
|
- XVFBARGS=":99 -ac -screen 0 1024x768x16"
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@ -14,6 +19,8 @@ matrix:
|
|||||||
env: DB=PGSQL PHPUNIT_TEST=1
|
env: DB=PGSQL PHPUNIT_TEST=1
|
||||||
- php: 7.1
|
- php: 7.1
|
||||||
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
|
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
|
||||||
|
- php: 7.1
|
||||||
|
env: DB=MYSQL BEHAT_TEST=1
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
# Init PHP
|
# Init PHP
|
||||||
@ -26,10 +33,21 @@ before_script:
|
|||||||
- if [[ $DB == PGSQL ]]; then composer require --prefer-dist --no-update silverstripe/postgresql:2.0.x-dev; fi
|
- if [[ $DB == PGSQL ]]; then composer require --prefer-dist --no-update silverstripe/postgresql:2.0.x-dev; fi
|
||||||
- composer update
|
- composer update
|
||||||
|
|
||||||
|
# Start behat services
|
||||||
|
- if [[ $BEHAT_TEST ]]; then echo 'SS_BASE_URL=http://localhost:8080/' >> .env; fi
|
||||||
|
- if [[ $BEHAT_TEST ]]; then mkdir artifacts; fi
|
||||||
|
- if [[ $BEHAT_TEST ]]; then sh -e /etc/init.d/xvfb start; sleep 3; fi
|
||||||
|
- if [[ $BEHAT_TEST ]]; then (vendor/bin/selenium-server-standalone > artifacts/selenium.log 2>&1 &); fi
|
||||||
|
- if [[ $BEHAT_TEST ]]; then (vendor/bin/serve --bootstrap-file cms/tests/behat/serve-bootstrap.php &> artifacts/serve.log &); fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
|
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
|
||||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
|
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
|
||||||
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist src/ tests/; fi
|
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=framework/phpcs.xml.dist src/ tests/; fi
|
||||||
|
- if [[ $BEHAT_TEST ]]; then vendor/bin/behat @contentreview; fi
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
||||||
|
|
||||||
|
after_failure:
|
||||||
|
- php ./framework/tests/behat/travis-upload-artifacts.php --if-env BEHAT_TEST,ARTIFACTS_BUCKET,ARTIFACTS_KEY,ARTIFACTS_SECRET --target-path $TRAVIS_REPO_SLUG/$TRAVIS_BUILD_ID/$TRAVIS_JOB_ID --artifacts-base-url https://s3.amazonaws.com/$ARTIFACTS_BUCKET/ --artifacts-path ./artifacts/
|
||||||
|
@ -6,3 +6,9 @@ file_filter = lang/<lang>.yml
|
|||||||
source_file = lang/en.yml
|
source_file = lang/en.yml
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = YML
|
type = YML
|
||||||
|
|
||||||
|
[silverstripe-asset-admin.master-js]
|
||||||
|
file_filter = client/lang/src/<lang>.json
|
||||||
|
source_file = client/lang/src/en.json
|
||||||
|
source_lang = en
|
||||||
|
type = KEYVALUEJSON
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
---
|
---
|
||||||
Name: contentreviewextensions
|
Name: contentreviewextensions
|
||||||
---
|
---
|
||||||
SilverStripe\CMS\Model\SiteTree:
|
SilverStripe\Admin\LeftAndMain:
|
||||||
extensions:
|
extensions:
|
||||||
- SilverStripe\ContentReview\Extensions\SiteTreeContentReview
|
- SilverStripe\ContentReview\Extensions\ContentReviewLeftAndMainExtension
|
||||||
SilverStripe\Security\Group:
|
|
||||||
extensions:
|
|
||||||
- SilverStripe\ContentReview\Extensions\ContentReviewOwner
|
|
||||||
SilverStripe\Security\Member:
|
|
||||||
extensions:
|
|
||||||
- SilverStripe\ContentReview\Extensions\ContentReviewOwner
|
|
||||||
SilverStripe\CMS\Controllers\CMSPageEditController:
|
SilverStripe\CMS\Controllers\CMSPageEditController:
|
||||||
extensions:
|
extensions:
|
||||||
- SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension
|
- SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension
|
||||||
|
|
||||||
SilverStripe\CMS\Controllers\CMSPageSettingsController:
|
SilverStripe\CMS\Controllers\CMSPageSettingsController:
|
||||||
extensions:
|
extensions:
|
||||||
- SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension
|
- SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension
|
||||||
|
|
||||||
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
|
extensions:
|
||||||
|
- SilverStripe\ContentReview\Extensions\SiteTreeContentReview
|
||||||
|
|
||||||
|
SilverStripe\Security\Group:
|
||||||
|
extensions:
|
||||||
|
- SilverStripe\ContentReview\Extensions\ContentReviewOwner
|
||||||
|
|
||||||
|
SilverStripe\Security\Member:
|
||||||
|
extensions:
|
||||||
|
- SilverStripe\ContentReview\Extensions\ContentReviewOwner
|
||||||
|
|
||||||
SilverStripe\SiteConfig\SiteConfig:
|
SilverStripe\SiteConfig\SiteConfig:
|
||||||
extensions:
|
extensions:
|
||||||
- SilverStripe\ContentReview\Extensions\ContentReviewDefaultSettings
|
- SilverStripe\ContentReview\Extensions\ContentReviewDefaultSettings
|
||||||
|
27
behat.yml
Normal file
27
behat.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
default:
|
||||||
|
suites:
|
||||||
|
contentreview:
|
||||||
|
paths:
|
||||||
|
- %paths.modules.contentreview%/tests/behat/features
|
||||||
|
contexts:
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\FeatureContext
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\CmsFormsContext
|
||||||
|
- SilverStripe\Framework\Tests\Behaviour\CmsUiContext
|
||||||
|
- SilverStripe\BehatExtension\Context\BasicContext
|
||||||
|
- SilverStripe\BehatExtension\Context\EmailContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\LoginContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\ThemeContext
|
||||||
|
- SilverStripe\CMS\Tests\Behaviour\FixtureContext:
|
||||||
|
# Note: double indent for args is intentional
|
||||||
|
- %paths.modules.contentreview%/tests/behat/features/files/
|
||||||
|
|
||||||
|
extensions:
|
||||||
|
SilverStripe\BehatExtension\MinkExtension:
|
||||||
|
default_session: selenium2
|
||||||
|
javascript_session: selenium2
|
||||||
|
selenium2:
|
||||||
|
browser: firefox
|
||||||
|
|
||||||
|
SilverStripe\BehatExtension\Extension:
|
||||||
|
screenshot_path: %paths.base%/artifacts/screenshots
|
||||||
|
bootstrap_file: "cms/tests/behat/serve-bootstrap.php"
|
2
client/dist/js/contentreview.js
vendored
2
client/dist/js/contentreview.js
vendored
@ -1 +1 @@
|
|||||||
!function(n){function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var e={};t.m=n,t.c=e,t.i=function(n){return n},t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{configurable:!1,enumerable:!0,get:i})},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=3)}([function(n,t){window.jQuery.entwine("ss",function(n){n('.review-notes input[name="action_savereview"]').entwine({onclick:function(){this._super(),n(".contentreview-tab .nav-link").trigger("click")}})})},function(n,t){window.jQuery.entwine("ss",function(n){n(".cms-edit-form #Form_EditForm_ContentReviewType_Holder").entwine({onmatch:function(){var n=this;this.find(".optionset :input").bind("change",function(t){n.show_option(t.target.value)});var t=this.find("input[name=ContentReviewType]:checked").val();this.show_option(t),this._super()},onunmatch:function(){return this._super()},show_option:function(n){"Custom"===n?this._custom():"Inherit"===n?this._inherited():this._disabled()},_custom:function(){n(".review-settings").show(),n(".field.custom-setting").show()},_inherited:function(){n(".review-settings").show(),n(".field.custom-setting").hide()},_disabled:function(){n(".review-settings").hide()}})})},function(n,t){window.jQuery.entwine("ss",function(n){function t(t){var e="ContentReviewOwnerID"+t,i=n("div.subsiteSpecificOwnerID"),o=0;for(o=0;o<i.length;o++)i[o].id===e?n(i[o]).show():n(i[o]).hide()}n("#Form_EditForm_SubsiteIDWithOwner").entwine({onmatch:function(){t(this.value)},change:function(){t(this.value)}})})},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=e(2),o=(e.n(i),e(0)),r=(e.n(o),e(1));e.n(r)}]);
|
!function(e){function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}var t={};n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=4)}([function(e,n){e.exports=jQuery},function(e,n,t){"use strict";var o=t(10),i=t.n(o),r=t(0),s=t.n(r),c=t(7),a=t.n(c),u=t(8),d=t.n(u),l=t(9),f=(t.n(l),t(6)),_=(t.n(f),t(5)),p=t.n(_),h=t.i(f.provideInjector)(p.a);s.a.entwine("ss",function(e){e(".cms-content-actions .content-review__button").entwine({onclick:function(n){n.preventDefault();var t=e("#content-review__dialog-wrapper");return t.length||(t=e('<div id="content-review__dialog-wrapper" />'),e("body").append(t)),t.open(),!1}}),e(".content-review-modal .content-review-modal__nav-link").entwine({onclick:function(n){n.preventDefault();var t=e(n.target);window.location=t.attr("href")}}),e("#content-review__dialog-wrapper").entwine({onunmatch:function(){this._clearModal()},open:function(){this._renderModal(!0)},close:function(){this._renderModal(!1)},_renderModal:function(n){var t=this,o=function(){return t.close()},r=function(){return t._handleSubmitModal.apply(t,arguments)},s=e("form.cms-edit-form :input[name=ID]").val(),c=window.ss.store,u=c.getState().config.sections.find(function(e){return"SilverStripe\\CMS\\Controllers\\CMSPageEditController"===e.name}),f=u.form.ReviewContentForm.schemaUrl+"/"+s,_=i.a._t("ContentReview.CONTENT_DUE_FOR_REVIEW","Content due for review");d.a.render(a.a.createElement(l.Provider,{store:c},a.a.createElement(h,{title:_,show:n,handleSubmit:r,handleHide:o,schemaUrl:f,bodyClassName:"modal__dialog",className:"content-review-modal",responseClassBad:"modal__response modal__response--error",responseClassGood:"modal__response modal__response--good",identifier:"ContentReview.CONTENT_DUE_FOR_REVIEW"})),this[0])},_clearModal:function(){d.a.unmountComponentAtNode(this[0])},_handleSubmitModal:function(e,n,t){return t()}})})},function(e,n,t){"use strict";var o=t(0);t.n(o).a.entwine("ss",function(e){e(".cms-edit-form #Form_EditForm_ContentReviewType_Holder").entwine({onmatch:function(){var e=this;this.find(".optionset :input").bind("change",function(n){e.show_option(n.target.value)});var n=this.find("input[name=ContentReviewType]:checked").val();this.show_option(n),this._super()},onunmatch:function(){return this._super()},show_option:function(e){"Custom"===e?this._custom():"Inherit"===e?this._inherited():this._disabled()},_custom:function(){e(".review-settings").show(),e(".field.custom-setting").show()},_inherited:function(){e(".review-settings").show(),e(".field.custom-setting").hide()},_disabled:function(){e(".review-settings").hide()}})})},function(e,n,t){"use strict";var o=t(0);t.n(o).a.entwine("ss",function(e){function n(n){var t="ContentReviewOwnerID"+n,o=e("div.subsiteSpecificOwnerID"),i=0;for(i=0;i<o.length;i++)o[i].id===t?e(o[i]).show():e(o[i]).hide()}e("#Form_EditForm_SubsiteIDWithOwner").entwine({onmatch:function(){n(this.value)},change:function(){n(this.value)}})})},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),t(1),t(2),t(3)},function(e,n){e.exports=FormBuilderModal},function(e,n){e.exports=Injector},function(e,n){e.exports=React},function(e,n){e.exports=ReactDom},function(e,n){e.exports=ReactRedux},function(e,n){e.exports=i18n}]);
|
2
client/dist/js/contentreview.js.map
vendored
2
client/dist/js/contentreview.js.map
vendored
@ -1 +1 @@
|
|||||||
{"version":3,"sources":["webpack:///webpack/bootstrap 2d6ce7e54f0ab0054f92","webpack:///./client/src/bundles/ContentReviewPopup.js","webpack:///./client/src/bundles/ContentReviewSettings.js","webpack:///./client/src/bundles/PagesDueForReview.js","webpack:///./client/src/bundles/bundle.js"],"names":["window","jQuery","entwine","$","onclick","_super","trigger","onmatch","self","find","bind","e","show_option","target","value","currentVal","attr","val","onunmatch","_custom","_inherited","_disabled","show","hide","showCorrectSubsiteIDDropdown","domid","ownerIDDropdowns","i","length","id","change"],"mappings":"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","file":"js/contentreview.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 3);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 2d6ce7e54f0ab0054f92","window.jQuery.entwine('ss', ($) => {\n $('.review-notes input[name=\"action_savereview\"]').entwine({\n /**\n * Close the review popup when the form is submitted\n */\n onclick() {\n this._super();\n $('.contentreview-tab .nav-link').trigger('click');\n }\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/ContentReviewPopup.js","window.jQuery.entwine('ss', ($) => {\n /**\n * Class: .cms-edit-form #Form_EditForm_ContentReviewType_Holder\n *\n * Toggle display of group dropdown in \"access\" tab,\n * based on selection of radiobuttons.\n */\n $('.cms-edit-form #Form_EditForm_ContentReviewType_Holder').entwine({\n // Constructor: onmatch\n onmatch() {\n const self = this;\n this.find('.optionset :input').bind('change', function (e) {\n self.show_option(e.target.value);\n });\n\n // initial state\n const currentVal = this.find(`input[name=${this.attr('id')}]:checked`).val();\n this.show_option(currentVal);\n this._super();\n },\n\n onunmatch() {\n return this._super();\n },\n\n show_option(value) {\n if (value === 'Custom') {\n this._custom();\n } else if (value === 'Inherit') {\n this._inherited();\n } else {\n this._disabled();\n }\n },\n\n _custom() {\n $('.review-settings').show();\n $('.field.custom-setting').show();\n },\n\n _inherited() {\n $('.review-settings').show();\n $('.field.custom-setting').hide();\n },\n\n _disabled() {\n $('.review-settings').hide();\n },\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/ContentReviewSettings.js","/**\n * @todo Re-validate this with Subsites\n */\nwindow.jQuery.entwine('ss', ($) => {\n // Hide all owner dropdowns except the one for the current subsite\n function showCorrectSubsiteIDDropdown(value) {\n const domid = `ContentReviewOwnerID${value}`;\n\n const ownerIDDropdowns = $('div.subsiteSpecificOwnerID');\n let i = 0;\n for (i = 0; i < ownerIDDropdowns.length; i++) {\n if (ownerIDDropdowns[i].id === domid) {\n $(ownerIDDropdowns[i]).show();\n } else {\n $(ownerIDDropdowns[i]).hide();\n }\n }\n }\n\n $('#Form_EditForm_SubsiteIDWithOwner').entwine({\n // Call method to show on report load\n onmatch() {\n showCorrectSubsiteIDDropdown(this.value);\n },\n\n // Call method to show on dropdown change\n change() {\n showCorrectSubsiteIDDropdown(this.value);\n },\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/PagesDueForReview.js","import 'bundles/PagesDueForReview.js';\nimport 'bundles/ContentReviewPopup.js';\nimport 'bundles/ContentReviewSettings.js';\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/bundle.js"],"sourceRoot":""}
|
{"version":3,"sources":["webpack:///webpack/bootstrap 2e84be8a35a52c0a303b","webpack:///external \"jQuery\"","webpack:///./client/src/bundles/ContentReviewForm.js","webpack:///./client/src/bundles/ContentReviewSettings.js","webpack:///./client/src/bundles/PagesDueForReview.js","webpack:///./client/src/bundles/bundle.js","webpack:///external \"FormBuilderModal\"","webpack:///external \"Injector\"","webpack:///external \"React\"","webpack:///external \"ReactDom\"","webpack:///external \"ReactRedux\"","webpack:///external \"i18n\""],"names":["InjectableFormBuilderModal","provideInjector","jQuery","entwine","$","onclick","e","preventDefault","dialog","length","append","open","$link","target","window","location","attr","onunmatch","_clearModal","_renderModal","close","show","handleHide","handleSubmit","_handleSubmitModal","id","val","store","ss","sectionConfigKey","sectionConfig","getState","config","sections","find","section","name","modalSchemaUrl","form","ReviewContentForm","schemaUrl","title","i18n","_t","ReactDOM","render","unmountComponentAtNode","data","action","submitFn","onmatch","self","bind","show_option","value","currentVal","_super","_custom","_inherited","_disabled","hide","showCorrectSubsiteIDDropdown","domid","ownerIDDropdowns","i","change"],"mappings":"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","file":"js/contentreview.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 4);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 2e84be8a35a52c0a303b","module.exports = jQuery;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"jQuery\"\n// module id = 0\n// module chunks = 0","import i18n from 'i18n';\nimport jQuery from 'jquery';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { provideInjector } from 'lib/Injector';\nimport FormBuilderModal from 'components/FormBuilderModal/FormBuilderModal';\n\nconst InjectableFormBuilderModal = provideInjector(FormBuilderModal);\n\n/**\n * \"Content due for review\" modal popup. See AddToCampaignForm.js in\n * silverstripe/admin for reference.\n */\njQuery.entwine('ss', ($) => {\n\t/**\n * Kick off a \"content due for review\" dialog from the CMS actions.\n */\n $('.cms-content-actions .content-review__button').entwine({\n onclick(e) {\n e.preventDefault();\n\n let dialog = $('#content-review__dialog-wrapper');\n\n if (!dialog.length) {\n dialog = $('<div id=\"content-review__dialog-wrapper\" />');\n $('body').append(dialog);\n }\n\n dialog.open();\n\n return false;\n },\n });\n\n // This is required because the React version of e.preventDefault() doesn't work\n // this is to prevent PJAX request to occur when clicking a link the modal\n $('.content-review-modal .content-review-modal__nav-link').entwine({\n onclick: (e) => {\n e.preventDefault();\n const $link = $(e.target);\n window.location = $link.attr('href');\n },\n });\n\n\t/**\n * Uses React-Bootstrap in order to replicate the bootstrap styling and JavaScript behaviour.\n */\n $('#content-review__dialog-wrapper').entwine({\n onunmatch() {\n // solves errors given by ReactDOM \"no matched root found\" error.\n this._clearModal();\n },\n\n open() {\n this._renderModal(true);\n },\n\n close() {\n this._renderModal(false);\n },\n\n _renderModal(show) {\n const handleHide = () => this.close();\n const handleSubmit = (...args) => this._handleSubmitModal(...args);\n const id = $('form.cms-edit-form :input[name=ID]').val();\n const store = window.ss.store;\n const sectionConfigKey = 'SilverStripe\\\\CMS\\\\Controllers\\\\CMSPageEditController';\n const sectionConfig = store.getState().config.sections\n .find((section) => section.name === sectionConfigKey);\n const modalSchemaUrl = `${sectionConfig.form.ReviewContentForm.schemaUrl}/${id}`;\n const title = i18n._t(\n 'SilverStripe\\\\ContentReview\\\\Models\\\\ContentReviewLog.CONTENT_DUE_FOR_REVIEW',\n 'Content due for review'\n );\n\n ReactDOM.render(\n <Provider store={store}>\n <InjectableFormBuilderModal\n title={title}\n show={show}\n handleSubmit={handleSubmit}\n handleHide={handleHide}\n schemaUrl={modalSchemaUrl}\n bodyClassName=\"modal__dialog\"\n className=\"content-review-modal\"\n responseClassBad=\"modal__response modal__response--error\"\n responseClassGood=\"modal__response modal__response--good\"\n identifier=\"SilverStripe\\\\ContentReview\\\\Models\\\\ContentReviewLog.CONTENT_DUE_FOR_REVIEW\"\n />\n </Provider>,\n this[0]\n );\n },\n\n _clearModal() {\n ReactDOM.unmountComponentAtNode(this[0]);\n // this.empty();\n },\n\n _handleSubmitModal(data, action, submitFn) {\n return submitFn();\n },\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/ContentReviewForm.js","import jQuery from 'jquery';\n\njQuery.entwine('ss', ($) => {\n /**\n * Class: .cms-edit-form #Form_EditForm_ContentReviewType_Holder\n *\n * Toggle display of group dropdown in \"access\" tab,\n * based on selection of radiobuttons.\n */\n $('.cms-edit-form #Form_EditForm_ContentReviewType_Holder').entwine({\n // Constructor: onmatch\n onmatch() {\n const self = this;\n this.find('.optionset :input').bind('change', (e) => {\n self.show_option(e.target.value);\n });\n\n // initial state\n const currentVal = this.find('input[name=ContentReviewType]:checked').val();\n this.show_option(currentVal);\n this._super();\n },\n\n onunmatch() {\n return this._super();\n },\n\n show_option(value) {\n if (value === 'Custom') {\n this._custom();\n } else if (value === 'Inherit') {\n this._inherited();\n } else {\n this._disabled();\n }\n },\n\n _custom() {\n $('.review-settings').show();\n $('.field.custom-setting').show();\n },\n\n _inherited() {\n $('.review-settings').show();\n $('.field.custom-setting').hide();\n },\n\n _disabled() {\n $('.review-settings').hide();\n },\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/ContentReviewSettings.js","import jQuery from 'jquery';\n\n/**\n * @todo Re-validate this with Subsites\n */\njQuery.entwine('ss', ($) => {\n // Hide all owner dropdowns except the one for the current subsite\n function showCorrectSubsiteIDDropdown(value) {\n const domid = `ContentReviewOwnerID${value}`;\n\n const ownerIDDropdowns = $('div.subsiteSpecificOwnerID');\n let i = 0;\n for (i = 0; i < ownerIDDropdowns.length; i++) {\n if (ownerIDDropdowns[i].id === domid) {\n $(ownerIDDropdowns[i]).show();\n } else {\n $(ownerIDDropdowns[i]).hide();\n }\n }\n }\n\n $('#Form_EditForm_SubsiteIDWithOwner').entwine({\n // Call method to show on report load\n onmatch() {\n showCorrectSubsiteIDDropdown(this.value);\n },\n\n // Call method to show on dropdown change\n change() {\n showCorrectSubsiteIDDropdown(this.value);\n },\n });\n});\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/PagesDueForReview.js","import 'bundles/ContentReviewForm.js';\nimport 'bundles/ContentReviewSettings.js';\nimport 'bundles/PagesDueForReview.js';\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/bundles/bundle.js","module.exports = FormBuilderModal;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"FormBuilderModal\"\n// module id = 5\n// module chunks = 0","module.exports = Injector;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"Injector\"\n// module id = 6\n// module chunks = 0","module.exports = React;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"React\"\n// module id = 7\n// module chunks = 0","module.exports = ReactDom;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"ReactDom\"\n// module id = 8\n// module chunks = 0","module.exports = ReactRedux;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"ReactRedux\"\n// module id = 9\n// module chunks = 0","module.exports = i18n;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"i18n\"\n// module id = 10\n// module chunks = 0"],"sourceRoot":""}
|
2
client/dist/styles/contentreview.css
vendored
2
client/dist/styles/contentreview.css
vendored
@ -1 +1 @@
|
|||||||
.cms .review-notes textarea{-webkit-box-sizing:border-box;box-sizing:border-box;height:60px;margin:4px 0;resize:none}.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .review-notes label.left{margin:0;padding:0}.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .review-notes .btn{width:auto}.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a{width:20px;height:20px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPlJREFUeNpi/P//PwM1AeOAGPg1h3EBiOae8j+BkFoWIi22pIoLgS5rAFIFQMwPFfoIxBNAGOjaDyQZCDTsApDSx2HXRSB2wGYoEx6X6ePxmT7U5QxEGYhLMTFqMLwMdJ0AkHpPZBwYAr19gZALDUhIdgLEeplsQBcDBUjQr4A3UoARQlbGBkYMI828TN/SBkeaFMSVjwl6GaqxEUmoEZ9hA1fAjkADL8dC6hTdxYOtTgG6DGedAnQtaXUK0DCCdQo2Q5nwuGyQ1ilA15FUpwC9PVqn0LJOAUYIWRkbGDHDpU7BkSYFceVjgl6GakSpU/AZNjRKbIAAAwDhCJTckjfR5wAAAABJRU5ErkJggg==) 50% no-repeat;background-position:0 0;text-indent:-9999px;padding:0;margin:2px 4px 0 12px}.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a:focus,.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a:hover{background-position:0 -20px}.cms .ui-tabs-panel.contentreview-tab .form-group .form__fieldgroup{margin:0;min-width:250px}.cms .ui-tabs-panel.contentreview-tab .form-group:after{border-bottom:0}
|
.content-review__button{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPlJREFUeNpi/P//PwM1AeOAGPg1h3EBiOae8j+BkFoWIi22pIoLgS5rAFIFQMwPFfoIxBNAGOjaDyQZCDTsApDSx2HXRSB2wGYoEx6X6ePxmT7U5QxEGYhLMTFqMLwMdJ0AkHpPZBwYAr19gZALDUhIdgLEeplsQBcDBUjQr4A3UoARQlbGBkYMI828TN/SBkeaFMSVjwl6GaqxEUmoEZ9hA1fAjkADL8dC6hTdxYOtTgG6DGedAnQtaXUK0DCCdQo2Q5nwuGyQ1ilA15FUpwC9PVqn0LJOAUYIWRkbGDHDpU7BkSYFceVjgl6GakSpU/AZNjRKbIAAAwDhCJTckjfR5wAAAABJRU5ErkJggg==) 50% no-repeat;background-position:0 0;display:inline-block;height:20px;margin:6px 4px 0 12px;padding:0;text-indent:-9999px;width:20px}.content-review__button:focus,.content-review__button:hover{background-position:0 -20px}
|
2
client/dist/styles/contentreview.css.map
vendored
2
client/dist/styles/contentreview.css.map
vendored
@ -1 +1 @@
|
|||||||
{"version":3,"sources":["webpack:///./client/src/styles/contentreview.scss?f47b","webpack:///./bundle.scss?6663"],"names":[],"mappings":"AAAA,4BAKI,oDACA,YACA,aACA,YCMH,yFDAO,SACA,UCUP,mFDNO,WCUP,mFDFK,WACA,YACA,uDACA,wBACA,oBACA,UACA,sBCML,kLDFO,4BCOP,oEDGK,SACA,gBCCL,wDDGK,gBCCL","file":"styles/contentreview.css","sourcesContent":[".cms {\n /**\n * The textarea for entering review comments\n */\n .review-notes textarea {\n box-sizing: border-box;\n height: 60px;\n margin: 4px 0;\n resize: none;\n }\n\n .ss-ui-action-tabset.action-menus.ss-tabset {\n .ui-tabs-panel .review-notes {\n label.left {\n margin: 0;\n padding: 0;\n }\n\n .btn {\n width: auto;\n }\n }\n\n /**\n * The icon showing that reviews are required (shows next to actions dropdown)\n */\n .ui-tabs-nav .contentreview-tab a {\n width: 20px;\n height: 20px;\n background: url('images/icon-bell.png') center center no-repeat;\n background-position: 0 0;\n text-indent: -9999px;\n padding: 0;\n margin: 2px 4px 0 12px;\n\n &:hover,\n &:focus {\n background-position: 0 -20px;\n }\n }\n }\n\n /**\n * The box container for the popup\n */\n .ui-tabs-panel.contentreview-tab .form-group {\n .form__fieldgroup {\n margin: 0;\n min-width: 250px;\n }\n\n &:after {\n border-bottom: 0;\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/styles/contentreview.scss",".cms {\n /**\n * The textarea for entering review comments\n */\n /**\n * The box container for the popup\n */\n}\n\n.cms .review-notes textarea {\n box-sizing: border-box;\n height: 60px;\n margin: 4px 0;\n resize: none;\n}\n\n.cms .ss-ui-action-tabset.action-menus.ss-tabset {\n /**\n * The icon showing that reviews are required (shows next to actions dropdown)\n */\n}\n\n.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .review-notes label.left {\n margin: 0;\n padding: 0;\n}\n\n.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .review-notes .btn {\n width: auto;\n}\n\n.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a {\n width: 20px;\n height: 20px;\n background: url(\"../images/icon-bell.png\") center center no-repeat;\n background-position: 0 0;\n text-indent: -9999px;\n padding: 0;\n margin: 2px 4px 0 12px;\n}\n\n.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a:hover,\n.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-nav .contentreview-tab a:focus {\n background-position: 0 -20px;\n}\n\n.cms .ui-tabs-panel.contentreview-tab .form-group .form__fieldgroup {\n margin: 0;\n min-width: 250px;\n}\n\n.cms .ui-tabs-panel.contentreview-tab .form-group:after {\n border-bottom: 0;\n}\n\n\n\n\n// WEBPACK FOOTER //\n// ./bundle.scss"],"sourceRoot":""}
|
{"version":3,"sources":["webpack:///./client/src/styles/ContentReviewForm.scss?a3bd","webpack:///./bundle.scss?6663"],"names":[],"mappings":"AAEA,wBACE,uDACA,wBACA,qBACA,YACA,sBACA,UACA,oBACA,WCDD,4DDKG,4BCAH","file":"styles/contentreview.css","sourcesContent":["// The bell button, shows up next to the major actions (save, publish, etc) when\n// viewing a page in the CMS\n.content-review__button {\n background: url(\"images/icon-bell.png\") center center no-repeat;\n background-position: 0 0;\n display: inline-block;\n height: 20px;\n margin: 6px 4px 0 12px;\n padding: 0;\n text-indent: -9999px;\n width: 20px;\n\n &:hover,\n &:focus {\n background-position: 0 -20px;\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./client/src/styles/ContentReviewForm.scss",".content-review__button {\n background: url(\"../images/icon-bell.png\") center center no-repeat;\n background-position: 0 0;\n display: inline-block;\n height: 20px;\n margin: 6px 4px 0 12px;\n padding: 0;\n text-indent: -9999px;\n width: 20px;\n}\n\n.content-review__button:hover,\n.content-review__button:focus {\n background-position: 0 -20px;\n}\n\n\n\n\n// WEBPACK FOOTER //\n// ./bundle.scss"],"sourceRoot":""}
|
3
client/lang/src/en.json
Normal file
3
client/lang/src/en.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ContentReview.CONTENT_DUE_FOR_REVIEW": "Content due for review"
|
||||||
|
}
|
101
client/src/bundles/ContentReviewForm.js
Normal file
101
client/src/bundles/ContentReviewForm.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import i18n from 'i18n';
|
||||||
|
import jQuery from 'jquery';
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { provideInjector } from 'lib/Injector';
|
||||||
|
import FormBuilderModal from 'components/FormBuilderModal/FormBuilderModal';
|
||||||
|
|
||||||
|
const InjectableFormBuilderModal = provideInjector(FormBuilderModal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Content due for review" modal popup. See AddToCampaignForm.js in
|
||||||
|
* silverstripe/admin for reference.
|
||||||
|
*/
|
||||||
|
jQuery.entwine('ss', ($) => {
|
||||||
|
/**
|
||||||
|
* Kick off a "content due for review" dialog from the CMS actions.
|
||||||
|
*/
|
||||||
|
$('.cms-content-actions .content-review__button').entwine({
|
||||||
|
onclick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let dialog = $('#content-review__dialog-wrapper');
|
||||||
|
|
||||||
|
if (!dialog.length) {
|
||||||
|
dialog = $('<div id="content-review__dialog-wrapper" />');
|
||||||
|
$('body').append(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.open();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is required because the React version of e.preventDefault() doesn't work
|
||||||
|
// this is to prevent PJAX request to occur when clicking a link the modal
|
||||||
|
$('.content-review-modal .content-review-modal__nav-link').entwine({
|
||||||
|
onclick: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const $link = $(e.target);
|
||||||
|
window.location = $link.attr('href');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses React-Bootstrap in order to replicate the bootstrap styling and JavaScript behaviour.
|
||||||
|
*/
|
||||||
|
$('#content-review__dialog-wrapper').entwine({
|
||||||
|
onunmatch() {
|
||||||
|
// solves errors given by ReactDOM "no matched root found" error.
|
||||||
|
this._clearModal();
|
||||||
|
},
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this._renderModal(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this._renderModal(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderModal(show) {
|
||||||
|
const handleHide = () => this.close();
|
||||||
|
const handleSubmit = (...args) => this._handleSubmitModal(...args);
|
||||||
|
const id = $('form.cms-edit-form :input[name=ID]').val();
|
||||||
|
const store = window.ss.store;
|
||||||
|
const sectionConfigKey = 'SilverStripe\\CMS\\Controllers\\CMSPageEditController';
|
||||||
|
const sectionConfig = store.getState().config.sections
|
||||||
|
.find((section) => section.name === sectionConfigKey);
|
||||||
|
const modalSchemaUrl = `${sectionConfig.form.ReviewContentForm.schemaUrl}/${id}`;
|
||||||
|
const title = i18n._t('ContentReview.CONTENT_DUE_FOR_REVIEW', 'Content due for review');
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<InjectableFormBuilderModal
|
||||||
|
title={title}
|
||||||
|
show={show}
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
handleHide={handleHide}
|
||||||
|
schemaUrl={modalSchemaUrl}
|
||||||
|
bodyClassName="modal__dialog"
|
||||||
|
className="content-review-modal"
|
||||||
|
responseClassBad="modal__response modal__response--error"
|
||||||
|
responseClassGood="modal__response modal__response--good"
|
||||||
|
identifier="ContentReview.CONTENT_DUE_FOR_REVIEW"
|
||||||
|
/>
|
||||||
|
</Provider>,
|
||||||
|
this[0]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearModal() {
|
||||||
|
ReactDOM.unmountComponentAtNode(this[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleSubmitModal(data, action, submitFn) {
|
||||||
|
return submitFn();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
@ -1,11 +0,0 @@
|
|||||||
window.jQuery.entwine('ss', ($) => {
|
|
||||||
$('.review-notes input[name="action_savereview"]').entwine({
|
|
||||||
/**
|
|
||||||
* Close the review popup when the form is submitted
|
|
||||||
*/
|
|
||||||
onclick() {
|
|
||||||
this._super();
|
|
||||||
$('.contentreview-tab .nav-link').trigger('click');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,4 +1,6 @@
|
|||||||
window.jQuery.entwine('ss', ($) => {
|
import jQuery from 'jquery';
|
||||||
|
|
||||||
|
jQuery.entwine('ss', ($) => {
|
||||||
/**
|
/**
|
||||||
* Class: .cms-edit-form #Form_EditForm_ContentReviewType_Holder
|
* Class: .cms-edit-form #Form_EditForm_ContentReviewType_Holder
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import jQuery from 'jquery';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Re-validate this with Subsites
|
* @todo Re-validate this with Subsites
|
||||||
*/
|
*/
|
||||||
window.jQuery.entwine('ss', ($) => {
|
jQuery.entwine('ss', ($) => {
|
||||||
// Hide all owner dropdowns except the one for the current subsite
|
// Hide all owner dropdowns except the one for the current subsite
|
||||||
function showCorrectSubsiteIDDropdown(value) {
|
function showCorrectSubsiteIDDropdown(value) {
|
||||||
const domid = `ContentReviewOwnerID${value}`;
|
const domid = `ContentReviewOwnerID${value}`;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import 'bundles/PagesDueForReview.js';
|
import 'bundles/ContentReviewForm.js';
|
||||||
import 'bundles/ContentReviewPopup.js';
|
|
||||||
import 'bundles/ContentReviewSettings.js';
|
import 'bundles/ContentReviewSettings.js';
|
||||||
|
import 'bundles/PagesDueForReview.js';
|
||||||
|
17
client/src/styles/ContentReviewForm.scss
Normal file
17
client/src/styles/ContentReviewForm.scss
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// The bell button, shows up next to the major actions (save, publish, etc) when
|
||||||
|
// viewing a page in the CMS
|
||||||
|
.content-review__button {
|
||||||
|
background: url("images/icon-bell.png") center center no-repeat;
|
||||||
|
background-position: 0 0;
|
||||||
|
display: inline-block;
|
||||||
|
height: 20px;
|
||||||
|
margin: 6px 4px 0 12px;
|
||||||
|
padding: 0;
|
||||||
|
text-indent: -9999px;
|
||||||
|
width: 20px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-position: 0 -20px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// See includePaths in webpack.config.js
|
// See includePaths in webpack.config.js
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
@import "contentreview";
|
@import "ContentReviewForm";
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
.cms {
|
|
||||||
// The textarea for entering review comments
|
|
||||||
.review-notes textarea {
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 60px;
|
|
||||||
margin: 4px 0;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ss-ui-action-tabset.action-menus.ss-tabset {
|
|
||||||
.ui-tabs-panel .review-notes {
|
|
||||||
label.left {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The icon showing that reviews are required (shows next to actions dropdown)
|
|
||||||
.ui-tabs-nav .contentreview-tab a {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background: url("images/icon-bell.png") center center no-repeat;
|
|
||||||
background-position: 0 0;
|
|
||||||
text-indent: -9999px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 2px 4px 0 12px;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-position: 0 -20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The box container for the popup
|
|
||||||
.ui-tabs-panel.contentreview-tab .form-group {
|
|
||||||
.form__fieldgroup {
|
|
||||||
margin: 0;
|
|
||||||
min-width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,10 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^5.7",
|
"phpunit/phpunit": "^5.7",
|
||||||
"squizlabs/php_codesniffer": "^3.0"
|
"squizlabs/php_codesniffer": "^3.0",
|
||||||
|
"silverstripe/behat-extension": "^3@dev",
|
||||||
|
"silverstripe/serve": "dev-master",
|
||||||
|
"se/selenium-server-standalone": "2.41.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"symbiote/silverstripe-queuedjobs": "Automatically schedules content review emails to be sent, only requiring one crontask to be created"
|
"symbiote/silverstripe-queuedjobs": "Automatically schedules content review emails to be sent, only requiring one crontask to be created"
|
||||||
@ -40,7 +43,7 @@
|
|||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"SilverStripe\\ContentReview\\": "src/",
|
"SilverStripe\\ContentReview\\": "src/",
|
||||||
"SilverStripe\\ContentReview\\Tests\\": "tests/"
|
"SilverStripe\\ContentReview\\Tests\\": "tests/php/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
|
@ -46,3 +46,10 @@ en:
|
|||||||
TITLE: 'Pages without a scheduled review.'
|
TITLE: 'Pages without a scheduled review.'
|
||||||
Review:
|
Review:
|
||||||
EMAILFROM_RIGHTTITLE: 'e.g: do-not-reply@site.com'
|
EMAILFROM_RIGHTTITLE: 'e.g: do-not-reply@site.com'
|
||||||
|
|
||||||
|
SilverStripe\ContentReview\Forms\ReviewContentHandler:
|
||||||
|
ContentDueForReview: Content due for review
|
||||||
|
MarkAsReviewedAction: Mark as reviewed
|
||||||
|
NoComments: (no comments)
|
||||||
|
Placeholder: Add comments (optional)
|
||||||
|
Success: Review successfully added
|
||||||
|
10
package.json
10
package.json
@ -23,6 +23,13 @@
|
|||||||
"url": "https://github.com/silverstripe/silverstripe-contentreview/issues"
|
"url": "https://github.com/silverstripe/silverstripe-contentreview/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/silverstripe/silverstripe-contentreview#readme",
|
"homepage": "https://github.com/silverstripe/silverstripe-contentreview#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "^3.2.1",
|
||||||
|
"react": "15.3.1",
|
||||||
|
"react-dom": "15.3.1",
|
||||||
|
"react-redux": "^4.4.1",
|
||||||
|
"redux": "https://registry.npmjs.org/redux/-/redux-3.0.5.tgz"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@silverstripe/webpack-config": "^0.2.5",
|
"@silverstripe/webpack-config": "^0.2.5",
|
||||||
"babel-core": "^6.24.1",
|
"babel-core": "^6.24.1",
|
||||||
@ -31,9 +38,6 @@
|
|||||||
"eslint": "^2.7.0",
|
"eslint": "^2.7.0",
|
||||||
"eslint-config-airbnb": "^6.2.0"
|
"eslint-config-airbnb": "^6.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"jquery": "^3.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^6.x"
|
"node": "^6.x"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<phpunit bootstrap="cms/tests/bootstrap.php" colors="true">
|
<phpunit bootstrap="cms/tests/bootstrap.php" colors="true">
|
||||||
<testsuite name="Default">
|
<testsuite name="Default">
|
||||||
<directory>tests/</directory>
|
<directory>tests/php/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<filter>
|
<filter>
|
||||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||||
|
@ -2,11 +2,16 @@
|
|||||||
|
|
||||||
namespace SilverStripe\ContentReview\Extensions;
|
namespace SilverStripe\ContentReview\Extensions;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\Admin\LeftAndMainExtension;
|
use SilverStripe\Admin\LeftAndMainExtension;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\ContentReview\Forms\ReviewContentHandler;
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\ORM\ValidationResult;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,39 +20,84 @@ use SilverStripe\Security\Security;
|
|||||||
*/
|
*/
|
||||||
class ContentReviewCMSExtension extends LeftAndMainExtension
|
class ContentReviewCMSExtension extends LeftAndMainExtension
|
||||||
{
|
{
|
||||||
/**
|
private static $allowed_actions = [
|
||||||
* @var array
|
'ReviewContentForm',
|
||||||
*/
|
'savereview',
|
||||||
private static $allowed_actions = array(
|
];
|
||||||
"savereview"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the review notes and redirect back to the page edit form.
|
* URL handler for the "content due for review" form
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return Form|null
|
||||||
|
*/
|
||||||
|
public function ReviewContentForm(HTTPRequest $request)
|
||||||
|
{
|
||||||
|
// Get ID either from posted back value, or url parameter
|
||||||
|
$id = $request->param('ID') ?: $request->postVar('ID');
|
||||||
|
return $this->getReviewContentForm($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a handler for "content due for review" forms, according to the given object ID
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return Form|null
|
||||||
|
*/
|
||||||
|
public function getReviewContentForm($id)
|
||||||
|
{
|
||||||
|
$page = $this->findRecord(['ID' => $id]);
|
||||||
|
$user = Security::getCurrentUser();
|
||||||
|
if (!$page->canEdit() || ($page->hasMethod('canBeReviewedBy') && !$page->canBeReviewedBy($user))) {
|
||||||
|
$this->owner->httpError(403, _t(
|
||||||
|
__CLASS__.'.ErrorItemPermissionDenied',
|
||||||
|
'It seems you don\'t have the necessary permissions to review this content'
|
||||||
|
));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = $this->getReviewContentHandler()->Form($page);
|
||||||
|
$form->setValidationResponseCallback(function (ValidationResult $errors) use ($form, $id) {
|
||||||
|
$schemaId = $this->owner->join_links($this->owner->Link('schema/ReviewContentForm'), $id);
|
||||||
|
return $this->getSchemaResponse($schemaId, $form, $errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action handler for processing the submitted content review
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param Form $form
|
* @param Form $form
|
||||||
*
|
* @return DBHTMLText|HTTPResponse|null
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws HTTPResponse_Exception
|
|
||||||
*/
|
*/
|
||||||
public function savereview($data, Form $form)
|
public function savereview($data, Form $form)
|
||||||
{
|
{
|
||||||
$page = $this->findRecord($data);
|
$page = $this->findRecord($data);
|
||||||
if (!$page->canEdit()) {
|
|
||||||
return Security::permissionFailure($this->owner);
|
$results = $this->getReviewContentHandler()->submitReview($page, $data);
|
||||||
|
if (is_null($results)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ($this->getSchemaRequested()) {
|
||||||
|
// Send extra "message" data with schema response
|
||||||
|
$extraData = ['message' => $results];
|
||||||
|
$schemaId = $this->owner->join_links($this->owner->Link('schema/ReviewContentForm'), $page->ID);
|
||||||
|
return $this->getSchemaResponse($schemaId, $form, null, $extraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$notes = (!empty($data["ReviewNotes"])
|
return $results;
|
||||||
? $data["ReviewNotes"]
|
}
|
||||||
: _t("ContentReview.NOCOMMENTS", "(no comments)"));
|
|
||||||
$page->addReviewNote(Member::currentUser(), $notes);
|
|
||||||
$page->advanceReviewDate();
|
|
||||||
|
|
||||||
$this->owner->getResponse()
|
/**
|
||||||
->addHeader("X-Status", _t("ContentReview.REVIEWSUCCESSFUL", "Content reviewed successfully"));
|
* Return a handler or reviewing content
|
||||||
return $this->owner->redirectBack();
|
*
|
||||||
|
* @return ReviewContentHandler
|
||||||
|
*/
|
||||||
|
protected function getReviewContentHandler()
|
||||||
|
{
|
||||||
|
return ReviewContentHandler::create($this->owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,24 +105,63 @@ class ContentReviewCMSExtension extends LeftAndMainExtension
|
|||||||
*
|
*
|
||||||
* @param array $data Form data
|
* @param array $data Form data
|
||||||
* @return SiteTree Record
|
* @return SiteTree Record
|
||||||
* @throws SS_HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
protected function findRecord($data)
|
protected function findRecord($data)
|
||||||
{
|
{
|
||||||
if (empty($data["ID"])) {
|
if (empty($data["ID"])) {
|
||||||
throw new HTTPResponse_Exception("No record ID", 404);
|
throw new HTTPResponse_Exception("No record ID", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$page = null;
|
$page = null;
|
||||||
$id = $data["ID"];
|
$id = $data["ID"];
|
||||||
if (is_numeric($id)) {
|
if (is_numeric($id)) {
|
||||||
$page = SiteTree::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$page || !$page->ID) {
|
if (!$page || !$page->ID) {
|
||||||
throw new HTTPResponse_Exception("Bad record ID #{$id}", 404);
|
throw new HTTPResponse_Exception("Bad record ID #{$id}", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current request has a X-Formschema-Request header set.
|
||||||
|
* Used by conditional logic that responds to validation results
|
||||||
|
*
|
||||||
|
* @todo Remove duplication. See https://github.com/silverstripe/silverstripe-admin/issues/240
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function getSchemaRequested()
|
||||||
|
{
|
||||||
|
$parts = $this->owner->getRequest()->getHeader(LeftAndMain::SCHEMA_HEADER);
|
||||||
|
return !empty($parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate schema for the given form based on the X-Formschema-Request header value
|
||||||
|
*
|
||||||
|
* @todo Remove duplication. See https://github.com/silverstripe/silverstripe-admin/issues/240
|
||||||
|
*
|
||||||
|
* @param string $schemaID ID for this schema. Required.
|
||||||
|
* @param Form $form Required for 'state' or 'schema' response
|
||||||
|
* @param ValidationResult $errors Required for 'error' response
|
||||||
|
* @param array $extraData Any extra data to be merged with the schema response
|
||||||
|
* @return HTTPResponse
|
||||||
|
*/
|
||||||
|
protected function getSchemaResponse($schemaID, $form = null, ValidationResult $errors = null, $extraData = [])
|
||||||
|
{
|
||||||
|
$parts = $this->owner->getRequest()->getHeader(LeftAndMain::SCHEMA_HEADER);
|
||||||
|
$data = $this->owner
|
||||||
|
->getFormSchema()
|
||||||
|
->getMultipartSchema($parts, $schemaID, $form, $errors);
|
||||||
|
|
||||||
|
if ($extraData) {
|
||||||
|
$data = array_merge($data, $extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = HTTPResponse::create(Convert::raw2json($data));
|
||||||
|
$response->addHeader('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
src/Extensions/ContentReviewLeftAndMainExtension.php
Normal file
20
src/Extensions/ContentReviewLeftAndMainExtension.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ContentReview\Extensions;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\LeftAndMainExtension;
|
||||||
|
|
||||||
|
class ContentReviewLeftAndMainExtension extends LeftAndMainExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Append content review schema configuration
|
||||||
|
*
|
||||||
|
* @param array &$clientConfig
|
||||||
|
*/
|
||||||
|
public function updateClientConfig(&$clientConfig)
|
||||||
|
{
|
||||||
|
$clientConfig['form']['ReviewContentForm'] = [
|
||||||
|
'schemaUrl' => $this->owner->Link('schema/ReviewContentForm')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ use SilverStripe\ContentReview\Jobs\ContentReviewNotificationJob;
|
|||||||
use SilverStripe\ContentReview\Models\ContentReviewLog;
|
use SilverStripe\ContentReview\Models\ContentReviewLog;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
@ -161,28 +162,16 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
|
|||||||
*/
|
*/
|
||||||
public function updateCMSActions(FieldList $actions)
|
public function updateCMSActions(FieldList $actions)
|
||||||
{
|
{
|
||||||
if ($this->canBeReviewedBy(Security::getCurrentUser())) {
|
if (!$this->canBeReviewedBy(Security::getCurrentUser())) {
|
||||||
Requirements::css('silverstripe/contentreview:client/dist/styles/contentreview.css');
|
return;
|
||||||
Requirements::javascript('silverstripe/contentreview:client/dist/js/contentreview.js');
|
|
||||||
|
|
||||||
$reviewTitle = LiteralField::create(
|
|
||||||
"ReviewContentNotesLabel",
|
|
||||||
"<label class=\"left\" for=\"Form_EditForm_ReviewNotes\">" . _t("ContentReview.CONTENTREVIEW", "Content due for review") . "</label>"
|
|
||||||
);
|
|
||||||
|
|
||||||
$ReviewNotes = LiteralField::create("ReviewNotes", "<textarea class=\"no-change-track\" id=\"Form_EditForm_ReviewNotes\" name=\"ReviewNotes\" placeholder=\"" . _t("ContentReview.COMMENTS", "(optional) Add comments...") . "\" class=\"text\"></textarea>");
|
|
||||||
|
|
||||||
$quickReviewAction = FormAction::create("savereview", _t("ContentReview.MARKREVIEWED", "Mark as reviewed"))
|
|
||||||
->addExtraClass('btn btn-primary');
|
|
||||||
|
|
||||||
$allFields = CompositeField::create($reviewTitle, $ReviewNotes, $quickReviewAction)
|
|
||||||
->addExtraClass('review-notes field');
|
|
||||||
|
|
||||||
$reviewTab = Tab::create('ReviewContent', $allFields);
|
|
||||||
$reviewTab->addExtraClass('contentreview-tab');
|
|
||||||
|
|
||||||
$actions->fieldByName('ActionMenus')->insertBefore($reviewTab, 'MoreOptions');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$module = ModuleLoader::getModule('silverstripe/contentreview');
|
||||||
|
Requirements::css($module->getRelativeResourcePath('client/dist/styles/contentreview.css'));
|
||||||
|
Requirements::javascript($module->getRelativeResourcePath('client/dist/js/contentreview.js'));
|
||||||
|
|
||||||
|
$reviewTab = LiteralField::create('ContentReviewButton', $this->owner->renderWith(__CLASS__ . '_button'));
|
||||||
|
$actions->insertAfter('MajorActions', $reviewTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,7 +329,8 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
|
|||||||
*/
|
*/
|
||||||
public function updateSettingsFields(FieldList $fields)
|
public function updateSettingsFields(FieldList $fields)
|
||||||
{
|
{
|
||||||
Requirements::javascript("silverstripe/contentreview:client/dist/js/contentreview.js");
|
$module = ModuleLoader::getModule('silverstripe/contentreview');
|
||||||
|
Requirements::javascript($module->getRelativeResourcePath('client/dist/js/contentreview.js'));
|
||||||
|
|
||||||
// Display read-only version only
|
// Display read-only version only
|
||||||
if (!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
|
if (!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
|
||||||
|
128
src/Forms/ReviewContentHandler.php
Normal file
128
src/Forms/ReviewContentHandler.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ContentReview\Forms;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
use SilverStripe\Forms\FieldList;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\FormAction;
|
||||||
|
use SilverStripe\Forms\HiddenField;
|
||||||
|
use SilverStripe\Forms\TextareaField;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\ValidationException;
|
||||||
|
use SilverStripe\Security\Security;
|
||||||
|
|
||||||
|
class ReviewContentHandler
|
||||||
|
{
|
||||||
|
use Injectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent controller for this form
|
||||||
|
*
|
||||||
|
* @var Controller
|
||||||
|
*/
|
||||||
|
protected $controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form name to use
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Controller $controller
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function __construct($controller = null, $name = 'ReviewContentForm')
|
||||||
|
{
|
||||||
|
$this->controller = $controller;
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap the form fields for the content review modal
|
||||||
|
*
|
||||||
|
* @param DataObject $object
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
public function Form($object)
|
||||||
|
{
|
||||||
|
$placeholder = _t(__CLASS__ . '.Placeholder', 'Add comments (optional)');
|
||||||
|
$title = _t(__CLASS__ . '.MarkAsReviewedAction', 'Mark as reviewed');
|
||||||
|
|
||||||
|
$fields = FieldList::create([
|
||||||
|
HiddenField::create('ID', null, $object->ID),
|
||||||
|
HiddenField::create('ClassName', null, $object->baseClass()),
|
||||||
|
TextareaField::create('Review', '')
|
||||||
|
->setAttribute('placeholder', $placeholder)
|
||||||
|
->setSchemaData(['attributes' => ['placeholder' => $placeholder]])
|
||||||
|
]);
|
||||||
|
|
||||||
|
$action = FormAction::create('savereview', $title)
|
||||||
|
->setTitle($title)
|
||||||
|
->setUseButtonTag(false)
|
||||||
|
->addExtraClass('review-content__action btn btn-primary');
|
||||||
|
$actions = FieldList::create([$action]);
|
||||||
|
|
||||||
|
$form = Form::create($this->controller, $this->name, $fields, $actions)
|
||||||
|
->setHTMLID('Form_EditForm_ReviewContent')
|
||||||
|
->addExtraClass('form--no-dividers review-content__form');
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate, and save the submitted form's review
|
||||||
|
*
|
||||||
|
* @param DataObject $record
|
||||||
|
* @param array $data
|
||||||
|
* @return HTTPResponse|string
|
||||||
|
* @throws ValidationException If the user cannot submit the review
|
||||||
|
*/
|
||||||
|
public function submitReview($record, $data)
|
||||||
|
{
|
||||||
|
if (!$this->canSubmitReview($record)) {
|
||||||
|
throw new ValidationException(_t(
|
||||||
|
__CLASS__ . '.ErrorReviewPermissionDenied',
|
||||||
|
'It seems you don\'t have the necessary permissions to submit a content review'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$notes = (!empty($data['Review']) ? $data['Review'] : _t(__CLASS__ . '.NoComments', '(no comments)'));
|
||||||
|
$record->addReviewNote(Security::getCurrentUser(), $notes);
|
||||||
|
$record->advanceReviewDate();
|
||||||
|
|
||||||
|
$request = $this->controller->getRequest();
|
||||||
|
$message = _t(__CLASS__ . '.Success', 'Review successfully added');
|
||||||
|
|
||||||
|
if ($request->getHeader('X-Formschema-Request')) {
|
||||||
|
return $message;
|
||||||
|
} elseif (Director::is_ajax()) {
|
||||||
|
$response = HTTPResponse::create($message, 200);
|
||||||
|
$response->addHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->controller->redirectBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can submit a review
|
||||||
|
*
|
||||||
|
* @param DataObject $record
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canSubmitReview($record)
|
||||||
|
{
|
||||||
|
if (!$record->canEdit()
|
||||||
|
|| !$record->hasMethod('canBeReviewedBy')
|
||||||
|
|| !$record->canBeReviewedBy(Security::getCurrentUser())
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
<div class="content-review__button-holder">
|
||||||
|
<a href="#" class="content-review__button"><%t ContentReview.CONTENTREVIEW 'Content due for review' %></a>
|
||||||
|
</div>
|
0
tests/behat/_manifest_exclude
Normal file
0
tests/behat/_manifest_exclude
Normal file
41
tests/behat/features/set-up-reviews.feature
Normal file
41
tests/behat/features/set-up-reviews.feature
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Feature: Set up reviews
|
||||||
|
As a CMS user
|
||||||
|
I can set up content reviews for my content
|
||||||
|
In order to ensure my content gets reviewed regularly
|
||||||
|
|
||||||
|
Background:
|
||||||
|
# Note: the review date is deliberately in the past
|
||||||
|
Given a "page" "Home" with "Content"="<p>Welcome</p>", "NextReviewDate"="01/01/2017", "ReviewPeriodDays"="1"
|
||||||
|
And I am logged in with "ADMIN" permissions
|
||||||
|
And I go to "admin/pages"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: I can set content review options
|
||||||
|
When I click on "Home" in the tree
|
||||||
|
And I click the "Settings" CMS tab
|
||||||
|
Then I should see a "Content Review" button
|
||||||
|
|
||||||
|
When I click the "Content Review" CMS tab
|
||||||
|
And I select "Custom settings" from "Options" input group
|
||||||
|
And I wait for 1 second
|
||||||
|
And I select "ADMIN group" from "Groups"
|
||||||
|
And I press "Save"
|
||||||
|
Then I should see a "Content due for review" button
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: I can enter a review in the modal
|
||||||
|
When I click on "Home" in the tree
|
||||||
|
And I click the "Settings" CMS tab
|
||||||
|
And I click the "Content Review" CMS tab
|
||||||
|
And I select "Custom settings" from "Options" input group
|
||||||
|
And I wait for 1 seconds
|
||||||
|
And I select "ADMIN group" from "Groups"
|
||||||
|
And I press "Save"
|
||||||
|
And I follow "Content due for review"
|
||||||
|
And I wait for 3 seconds
|
||||||
|
Then I should see a "Mark as reviewed" button
|
||||||
|
|
||||||
|
When I fill in "Review" with "LGTM"
|
||||||
|
And I press "Mark as reviewed"
|
||||||
|
And I wait for 3 seconds
|
||||||
|
Then I should see "Review successfully added"
|
@ -90,34 +90,6 @@ class ContentReviewCMSPageEditControllerTest extends ContentReviewBaseTest
|
|||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveReview()
|
|
||||||
{
|
|
||||||
/** @var Member $author */
|
|
||||||
$author = $this->objFromFixture(Member::class, "author");
|
|
||||||
|
|
||||||
$this->logInAs($author);
|
|
||||||
|
|
||||||
/** @var Page|SiteTreeContentReview $page */
|
|
||||||
$page = $this->objFromFixture(Page::class, "home");
|
|
||||||
|
|
||||||
$data = array(
|
|
||||||
"action_savereview" => 1,
|
|
||||||
"ID" => $page->ID,
|
|
||||||
"ReviewNotes" => "This is the best page ever",
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->get('admin/pages/edit/show/' . $page->ID);
|
|
||||||
$response = $this->post($this->getFormAction($page), $data);
|
|
||||||
|
|
||||||
$this->assertEquals("OK", $response->getStatusDescription());
|
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
|
||||||
$this->assertEquals(1, $page->ReviewLogs()->count());
|
|
||||||
|
|
||||||
$reviewLog = $page->ReviewLogs()->first();
|
|
||||||
|
|
||||||
$this->assertEquals($data["ReviewNotes"], $reviewLog->Note);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a CMS page edit form action via using a dummy request and session
|
* Return a CMS page edit form action via using a dummy request and session
|
||||||
*
|
*
|
93
tests/php/Extensions/ContentReviewCMSExtensionTest.php
Normal file
93
tests/php/Extensions/ContentReviewCMSExtensionTest.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ContentReview\Tests\Extensions;
|
||||||
|
|
||||||
|
use SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension;
|
||||||
|
use SilverStripe\ContentReview\Forms\ReviewContentHandler;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
|
class ContentReviewCMSExtensionTest extends SapphireTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test that ReviewContentForm finds an ID parameter then returns the result of getReviewContentForm
|
||||||
|
* with the passed ID
|
||||||
|
*/
|
||||||
|
public function testReviewContentForm()
|
||||||
|
{
|
||||||
|
$mock = $this->getMockBuilder(ContentReviewCMSExtension::class)
|
||||||
|
->setMethods(['getReviewContentForm'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mock->expects($this->once())->method('getReviewContentForm')->with(123)->willReturn(true);
|
||||||
|
|
||||||
|
$request = new HTTPRequest('GET', '/', [], ['ID' => 123]);
|
||||||
|
$result = $mock->ReviewContentForm($request);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException SilverStripe\Control\HTTPResponse_Exception
|
||||||
|
* @expectedExceptionMessage Bad record ID #1234
|
||||||
|
*/
|
||||||
|
public function testGetReviewContentFormThrowsExceptionWhenPageNotFound()
|
||||||
|
{
|
||||||
|
(new ContentReviewCMSExtension)->getReviewContentForm(1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException SilverStripe\Control\HTTPResponse_Exception
|
||||||
|
* @expectedExceptionMessage It seems you don't have the necessary permissions to review this content
|
||||||
|
*/
|
||||||
|
public function testGetReviewContentFormThrowsExceptionWhenObjectCannotBeReviewed()
|
||||||
|
{
|
||||||
|
$this->logOut();
|
||||||
|
|
||||||
|
$mock = $this->getMockBuilder(ContentReviewCMSExtension::class)
|
||||||
|
->setMethods(['findRecord'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mock->setOwner(new Controller);
|
||||||
|
|
||||||
|
// Return a DataObject without the content review extension applied
|
||||||
|
$mock->expects($this->once())->method('findRecord')->with(['ID' => 123])->willReturn(new Member);
|
||||||
|
|
||||||
|
$mock->getReviewContentForm(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that savereview() calls the ReviewContentHandler and passes the data to it
|
||||||
|
*/
|
||||||
|
public function testSaveReviewCallsHandler()
|
||||||
|
{
|
||||||
|
$mock = $this->getMockBuilder(ContentReviewCMSExtension::class)
|
||||||
|
->setMethods(['findRecord', 'getReviewContentHandler'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mock->setOwner(new Controller);
|
||||||
|
|
||||||
|
$mockPage = (object) ['ID' => 123];
|
||||||
|
$mock->expects($this->once())->method('findRecord')->willReturn($mockPage);
|
||||||
|
|
||||||
|
$mockHandler = $this->getMockBuilder(ReviewContentHandler::class)
|
||||||
|
->setMethods(['submitReview'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mockHandler->expects($this->once())
|
||||||
|
->method('submitReview')
|
||||||
|
->with($mockPage, ['foo'])
|
||||||
|
->willReturn('Success');
|
||||||
|
|
||||||
|
$mock->expects($this->once())->method('getReviewContentHandler')->willReturn($mockHandler);
|
||||||
|
|
||||||
|
$form = $this->getMockBuilder(Form::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$result = $mock->savereview(['foo'], $form);
|
||||||
|
$this->assertSame('Success', $result);
|
||||||
|
}
|
||||||
|
}
|
84
tests/php/Forms/ReviewContentHandlerTest.php
Normal file
84
tests/php/Forms/ReviewContentHandlerTest.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ContentReview\Tests\Forms;
|
||||||
|
|
||||||
|
use Page;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use SilverStripe\ContentReview\Forms\ReviewContentHandler;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\HiddenField;
|
||||||
|
use SilverStripe\Forms\TextareaField;
|
||||||
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
|
class ReviewContentHandlerTest extends SapphireTest
|
||||||
|
{
|
||||||
|
public function testForm()
|
||||||
|
{
|
||||||
|
$page = Page::create();
|
||||||
|
$page->Title = 'Test';
|
||||||
|
$page->write();
|
||||||
|
|
||||||
|
$form = ReviewContentHandler::create()->Form($page);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(Form::class, $form);
|
||||||
|
$this->assertSame('ReviewContentForm', $form->getName());
|
||||||
|
|
||||||
|
$this->assertInstanceOf(HiddenField::class, $form->Fields()->fieldByName('ID'));
|
||||||
|
$this->assertInstanceOf(HiddenField::class, $form->Fields()->fieldByName('ClassName'));
|
||||||
|
$this->assertInstanceOf(TextareaField::class, $form->Fields()->fieldByName('Review'));
|
||||||
|
|
||||||
|
$saveAction = $form->Actions()->first();
|
||||||
|
$this->assertNotNull($saveAction);
|
||||||
|
$this->assertTrue($saveAction->hasClass('review-content__action'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException SilverStripe\ORM\ValidationException
|
||||||
|
* @expectedExceptionMessage It seems you don't have the necessary permissions to submit a content review
|
||||||
|
*/
|
||||||
|
public function testExceptionThrownWhenSubmittingReviewForInvalidObject()
|
||||||
|
{
|
||||||
|
ReviewContentHandler::create()->submitReview(new Member, ['foo' => 'bar']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddReviewNoteCalledWhenSubmittingReview()
|
||||||
|
{
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
$controller = new Controller;
|
||||||
|
$request = new HTTPRequest('GET', '/');
|
||||||
|
$controller->setRequest($request);
|
||||||
|
Injector::inst()->registerservice($request);
|
||||||
|
|
||||||
|
$mock = $this->getMockBuilder(ReviewContentHandler::class)
|
||||||
|
->setConstructorArgs([$controller])
|
||||||
|
->setMethods(['canSubmitReview'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$mock->expects($this->exactly(3))->method('canSubmitReview')->willReturn(true);
|
||||||
|
|
||||||
|
// Via CMS
|
||||||
|
$request->addHeader('X-Formschema-Request', true);
|
||||||
|
$result = $mock->submitReview(new SiteTree, ['Review' => 'testing']);
|
||||||
|
$this->assertSame('Review successfully added', $result);
|
||||||
|
$request->removeHeader('X-Formschema-Request');
|
||||||
|
|
||||||
|
// Via AJAX
|
||||||
|
$request->addHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
|
$result = $mock->submitReview(new SiteTree, ['Review' => 'testing']);
|
||||||
|
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||||
|
$this->assertSame(200, $result->getStatusCode());
|
||||||
|
$this->assertSame('Review successfully added', $result->getBody());
|
||||||
|
$request->removeHeader('X-Requested-With');
|
||||||
|
|
||||||
|
// Default
|
||||||
|
$result = $mock->submitReview(new SiteTree, ['Review' => 'testing']);
|
||||||
|
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||||
|
$this->assertSame(302, $result->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ use SilverStripe\ContentReview\Extensions\SiteTreeContentReview;
|
|||||||
use SilverStripe\ContentReview\Extensions\ContentReviewOwner;
|
use SilverStripe\ContentReview\Extensions\ContentReviewOwner;
|
||||||
use SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension;
|
use SilverStripe\ContentReview\Extensions\ContentReviewCMSExtension;
|
||||||
use SilverStripe\ContentReview\Extensions\ContentReviewDefaultSettings;
|
use SilverStripe\ContentReview\Extensions\ContentReviewDefaultSettings;
|
||||||
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Security\Group;
|
use SilverStripe\Security\Group;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\SiteConfig\SiteConfig;
|
use SilverStripe\SiteConfig\SiteConfig;
|
||||||
@ -294,19 +295,19 @@ class SiteTreeContentReviewTest extends ContentReviewBaseTest
|
|||||||
|
|
||||||
public function testReviewActionVisibleForAuthor()
|
public function testReviewActionVisibleForAuthor()
|
||||||
{
|
{
|
||||||
DBDatetime::set_mock_now("2020-03-01 12:00:00");
|
DBDatetime::set_mock_now('2020-03-01 12:00:00');
|
||||||
|
|
||||||
/** @var Page|SiteTreeContentReview $page */
|
/** @var Page|SiteTreeContentReview $page */
|
||||||
$page = $this->objFromFixture(Page::class, "contact");
|
$page = $this->objFromFixture(Page::class, 'contact');
|
||||||
|
|
||||||
/** @var Member $author */
|
/** @var Member $author */
|
||||||
$author = $this->objFromFixture(Member::class, "author");
|
$author = $this->objFromFixture(Member::class, 'author');
|
||||||
|
|
||||||
$this->logInAs($author);
|
$this->logInAs($author);
|
||||||
|
|
||||||
$fields = $page->getCMSActions();
|
$fields = $page->getCMSActions();
|
||||||
|
|
||||||
$this->assertNotNull($fields->fieldByName("ActionMenus.ReviewContent"));
|
$this->assertInstanceOf(LiteralField::class, $fields->fieldByName('ContentReviewButton'));
|
||||||
|
|
||||||
DBDatetime::clear_mock_now();
|
DBDatetime::clear_mock_now();
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const webpack = require('webpack');
|
|
||||||
// Import the core config
|
// Import the core config
|
||||||
const webpackConfig = require('@silverstripe/webpack-config');
|
const webpackConfig = require('@silverstripe/webpack-config');
|
||||||
const {
|
const {
|
||||||
@ -33,6 +32,7 @@ const config = [
|
|||||||
},
|
},
|
||||||
devtool: (ENV !== 'production') ? 'source-map' : '',
|
devtool: (ENV !== 'production') ? 'source-map' : '',
|
||||||
resolve: resolveJS(ENV, PATHS),
|
resolve: resolveJS(ENV, PATHS),
|
||||||
|
externals: externalJS(ENV, PATHS),
|
||||||
module: moduleJS(ENV, PATHS),
|
module: moduleJS(ENV, PATHS),
|
||||||
plugins: pluginJS(ENV, PATHS),
|
plugins: pluginJS(ENV, PATHS),
|
||||||
},
|
},
|
||||||
|
118
yarn.lock
118
yarn.lock
@ -197,6 +197,10 @@ arrify@^1.0.0:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||||
|
|
||||||
|
asap@~2.0.3:
|
||||||
|
version "2.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
|
|
||||||
asn1.js@^4.0.0:
|
asn1.js@^4.0.0:
|
||||||
version "4.9.1"
|
version "4.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
|
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
|
||||||
@ -1171,6 +1175,10 @@ convert-source-map@^1.1.1, convert-source-map@^1.5.0:
|
|||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
|
||||||
|
|
||||||
|
core-js@^1.0.0:
|
||||||
|
version "1.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||||
|
|
||||||
core-js@^2.4.0, core-js@^2.5.0:
|
core-js@^2.4.0, core-js@^2.5.0:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
|
||||||
@ -1218,6 +1226,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
|
|||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
|
create-react-class@^15.5.1:
|
||||||
|
version "15.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.9"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
cross-spawn@^3.0.0:
|
cross-spawn@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
||||||
@ -1463,6 +1479,12 @@ emojis-list@^2.0.0:
|
|||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
||||||
|
|
||||||
|
encoding@^0.1.11:
|
||||||
|
version "0.1.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||||
|
dependencies:
|
||||||
|
iconv-lite "~0.4.13"
|
||||||
|
|
||||||
enhanced-resolve@^3.3.0:
|
enhanced-resolve@^3.3.0:
|
||||||
version "3.4.1"
|
version "3.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
|
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
|
||||||
@ -1708,6 +1730,18 @@ fastparse@^1.1.1:
|
|||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
||||||
|
|
||||||
|
fbjs@^0.8.4, fbjs@^0.8.9:
|
||||||
|
version "0.8.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.15.tgz#4f0695fdfcc16c37c0b07facec8cb4c4091685b9"
|
||||||
|
dependencies:
|
||||||
|
core-js "^1.0.0"
|
||||||
|
isomorphic-fetch "^2.1.1"
|
||||||
|
loose-envify "^1.0.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
promise "^7.1.1"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
ua-parser-js "^0.7.9"
|
||||||
|
|
||||||
figures@^1.3.5:
|
figures@^1.3.5:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||||
@ -2046,6 +2080,10 @@ hoek@2.x.x:
|
|||||||
version "2.16.3"
|
version "2.16.3"
|
||||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
|
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
|
||||||
|
|
||||||
|
hoist-non-react-statics@^1.0.3:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
|
||||||
|
|
||||||
home-or-tmp@^2.0.0:
|
home-or-tmp@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
|
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
|
||||||
@ -2073,6 +2111,10 @@ https-browserify@0.0.1:
|
|||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
|
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
|
||||||
|
|
||||||
|
iconv-lite@~0.4.13:
|
||||||
|
version "0.4.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||||
|
|
||||||
icss-replace-symbols@^1.1.0:
|
icss-replace-symbols@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
||||||
@ -2161,7 +2203,7 @@ interpret@^1.0.0:
|
|||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
|
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
|
||||||
|
|
||||||
invariant@^2.2.2:
|
invariant@^2.0.0, invariant@^2.2.2:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
|
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2304,6 +2346,10 @@ is-resolvable@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tryit "^1.0.1"
|
tryit "^1.0.1"
|
||||||
|
|
||||||
|
is-stream@^1.0.1:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
|
|
||||||
is-svg@^2.0.0:
|
is-svg@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
|
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
|
||||||
@ -2336,6 +2382,13 @@ isobject@^3.0.1:
|
|||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
|
|
||||||
|
isomorphic-fetch@^2.1.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||||
|
dependencies:
|
||||||
|
node-fetch "^1.0.1"
|
||||||
|
whatwg-fetch ">=0.10.0"
|
||||||
|
|
||||||
isstream@~0.1.2:
|
isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
@ -2621,7 +2674,7 @@ lodash.uniq@^4.5.0:
|
|||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
|
||||||
lodash@4.17.4, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.4:
|
lodash@4.17.4, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4:
|
||||||
version "4.17.4"
|
version "4.17.4"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||||
|
|
||||||
@ -2629,7 +2682,7 @@ longest@^1.0.1:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
|
|
||||||
loose-envify@^1.0.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2807,6 +2860,13 @@ nan@^2.3.0, nan@^2.3.2:
|
|||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
|
||||||
|
|
||||||
|
node-fetch@^1.0.1:
|
||||||
|
version "1.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||||
|
dependencies:
|
||||||
|
encoding "^0.1.11"
|
||||||
|
is-stream "^1.0.1"
|
||||||
|
|
||||||
node-gyp@^3.3.1:
|
node-gyp@^3.3.1:
|
||||||
version "3.6.2"
|
version "3.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60"
|
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60"
|
||||||
@ -2952,7 +3012,7 @@ oauth-sign@~0.8.1:
|
|||||||
version "0.8.2"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
||||||
|
|
||||||
object-assign@^4.0.1, object-assign@^4.1.0:
|
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
||||||
@ -3440,6 +3500,19 @@ progress@^1.1.8:
|
|||||||
version "1.1.8"
|
version "1.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
||||||
|
|
||||||
|
promise@^7.1.1:
|
||||||
|
version "7.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
|
||||||
|
dependencies:
|
||||||
|
asap "~2.0.3"
|
||||||
|
|
||||||
|
prop-types@^15.5.4:
|
||||||
|
version "15.5.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.9"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
|
||||||
prr@~0.0.0:
|
prr@~0.0.0:
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
|
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
|
||||||
@ -3515,6 +3588,29 @@ rc@^1.1.7:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-dom@15.3.1:
|
||||||
|
version "15.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.1.tgz#6d42cd2b64c8c5e0b693f3ffaec301e6e627e24e"
|
||||||
|
|
||||||
|
react-redux@^4.4.1:
|
||||||
|
version "4.4.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-4.4.8.tgz#e7bc1dd100e8b64e96ac8212db113239b9e2e08f"
|
||||||
|
dependencies:
|
||||||
|
create-react-class "^15.5.1"
|
||||||
|
hoist-non-react-statics "^1.0.3"
|
||||||
|
invariant "^2.0.0"
|
||||||
|
lodash "^4.2.0"
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
prop-types "^15.5.4"
|
||||||
|
|
||||||
|
react@15.3.1:
|
||||||
|
version "15.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-15.3.1.tgz#f78501ed8c2ec6e6e31c3223652e97f1369d2bd6"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.4"
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
|
||||||
read-pkg-up@^1.0.1:
|
read-pkg-up@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||||
@ -3580,6 +3676,10 @@ reduce-function-call@^1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match "^0.4.2"
|
balanced-match "^0.4.2"
|
||||||
|
|
||||||
|
"redux@https://registry.npmjs.org/redux/-/redux-3.0.5.tgz":
|
||||||
|
version "3.0.5"
|
||||||
|
resolved "https://registry.npmjs.org/redux/-/redux-3.0.5.tgz#f3f23f780b98c8dd7f84b9187ab5f86fe90199b8"
|
||||||
|
|
||||||
regenerate@^1.2.1:
|
regenerate@^1.2.1:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
|
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
|
||||||
@ -3857,7 +3957,7 @@ set-immediate-shim@^1.0.1:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
||||||
|
|
||||||
setimmediate@^1.0.4:
|
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
|
|
||||||
@ -4191,6 +4291,10 @@ typedarray@^0.0.6:
|
|||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
|
|
||||||
|
ua-parser-js@^0.7.9:
|
||||||
|
version "0.7.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca"
|
||||||
|
|
||||||
uglify-js@^2.8.27:
|
uglify-js@^2.8.27:
|
||||||
version "2.8.29"
|
version "2.8.29"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
|
||||||
@ -4338,6 +4442,10 @@ webpack@^2.6.1:
|
|||||||
webpack-sources "^1.0.1"
|
webpack-sources "^1.0.1"
|
||||||
yargs "^6.0.0"
|
yargs "^6.0.0"
|
||||||
|
|
||||||
|
whatwg-fetch@>=0.10.0:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
||||||
|
|
||||||
whet.extend@~0.9.9:
|
whet.extend@~0.9.9:
|
||||||
version "0.9.9"
|
version "0.9.9"
|
||||||
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
|
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
|
||||||
|
Loading…
Reference in New Issue
Block a user