Compare commits

...

208 Commits

Author SHA1 Message Date
dependabot[bot] 8bf1947809
Merge pull request #239 from silverstripe/dependabot/npm_and_yarn/qs-6.5.3 2022-12-10 10:56:50 +00:00
dependabot[bot] 302057a80c
Bump qs from 6.5.2 to 6.5.3
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-10 10:46:58 +00:00
dependabot[bot] a0349be6d1
Merge pull request #232 from silverstripe/dependabot/npm_and_yarn/y18n-3.2.2 2021-05-14 10:39:49 +00:00
dependabot[bot] f46701573f
Bump y18n from 3.2.1 to 3.2.2
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 04:31:10 +00:00
dependabot[bot] 6373ea90a1
Merge pull request #230 from silverstripe/dependabot/npm_and_yarn/ini-1.3.8 2020-12-31 06:44:52 +00:00
dependabot[bot] 9fa27cccd2
Bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-12 02:38:08 +00:00
Garion Herman 6e7a7f15f1
Merge pull request #229 from silverstripe/dependabot/npm_and_yarn/node-sass-4.13.1
Bump node-sass from 4.13.0 to 4.13.1
2020-11-17 16:23:27 +13:00
dependabot[bot] 04a81b6bb9
Bump node-sass from 4.13.0 to 4.13.1
Bumps [node-sass](https://github.com/sass/node-sass) from 4.13.0 to 4.13.1.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.13.0...v4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 00:03:21 +00:00
dependabot[bot] d4562c381e
Merge pull request #228 from silverstripe/dependabot/npm_and_yarn/elliptic-6.5.3 2020-09-04 00:01:40 +00:00
dependabot[bot] b638e43348
Bump elliptic from 6.5.1 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.1 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.1...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-01 06:25:45 +00:00
dependabot[bot] 4a11c7c0c7
Merge pull request #227 from silverstripe/dependabot/npm_and_yarn/lodash-4.17.19 2020-07-21 20:23:25 +00:00
dependabot[bot] f3c1614aea
Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-18 23:26:09 +00:00
Aaron Carlino 42540b6a10
META: Add github action to build docs 2019-12-19 13:56:43 +13:00
Aaron Carlino 85dce59716
Merge pull request #226 from silverstripe/pulls/master/new-docs
Update userguide for compliance with new docs site
2019-12-19 12:49:04 +13:00
Steve Boyd 9f23ed4769 Merge branch '2.2' 2019-12-16 15:07:03 +13:00
Steve Boyd 3b21e4cf88 Merge branch '2.1' into 2.2 2019-12-16 15:05:00 +13:00
Serge Latyntsev 37c79d257a
Merge pull request #225 from creative-commoners/pulls/2.1/update-unit-test-date
Change date on unit test - 2.1
2019-12-16 14:57:40 +13:00
Serge Latyntsev 6c1fcf6266
Merge pull request #223 from creative-commoners/pulls/master/update-travis
Fix travis build
2019-12-16 14:55:53 +13:00
Serge Latyntcev 402dc77ed6 Require spyc 0.6.2 or less (0.6.3 requires at least php 5.6) 2019-12-16 12:04:38 +13:00
Steve Boyd 209737c4b6 Change date on unit test 2019-12-16 11:50:40 +13:00
Steve Boyd a09499b4bd Use 3.7 branch, change to dms dir before running yarn, install dev dependencies 2019-12-16 11:19:52 +13:00
Maxime Rainville 09fea7961e
Merge pull request #222 from creative-commoners/pulls/master/update-unit-test-date
Change date on unit test
2019-12-12 17:45:11 +13:00
Steve Boyd e2dc05b0ff Change date on unit test 2019-12-12 17:33:38 +13:00
Aaron Carlino 9cf74f57c2 Update userguide for compliance with new docs site 2019-12-06 14:47:40 +13:00
Maxime Rainville f2ef1b82ea
Merge pull request #219 from creative-commoners/pulls/master/add-lock-file
Add yarn.lock file
2019-12-02 17:28:44 +13:00
Maxime Rainville 8aba8cd8d7 Upgrade travis build set up 2019-11-13 12:40:21 +13:00
Maxime Rainville 49239899fa Add yarn.lock file 2019-11-13 12:00:59 +13:00
Loz Calver 945119a1da
Update branch alias 2019-01-02 15:33:31 +00:00
Robbie Averill e7535a6af6
Merge pull request #217 from kinglozzer/php72
PHP 7.2 compatibility
2019-01-02 12:58:39 +00:00
Loz Calver 1fc11a7854 PHP 7.2 compatibility 2019-01-02 11:08:48 +00:00
Robbie Averill 63b6fa567a
Merge pull request #216 from dnadesign/2.1
FIX: Ensure Version gridfield has correct field formatting and variables
2018-08-21 09:47:32 +12:00
James Ayers 42275f5d13 FIX: Escape double quotes so Field can be safely eval()’d 2018-08-20 20:23:14 +12:00
Daniel Hensby c0fbe98851
Merge pull request #214 from InnisMaggiore/dmc/download-behavior-on-write
Save default DownloadBehaviour on a DMSDocument when initially uploading a new one
2018-07-06 16:57:19 +01:00
Dave Collins cf278fd8e2 Save default DownloadBehaviour on a DMSDocument when initially uploading a new one
- array key check for behavior before using
2018-07-06 08:59:10 -04:00
Dave Collins 52772ae879 documentation fix for DownloadBehavior fix 2018-07-05 12:06:14 -04:00
Dave Collins 5561e4a7af minor formatting fix 2018-07-05 12:04:33 -04:00
Dave Collins 71c09244f7 Make sure default DownloadBehavior is stored when initially uploading a document 2018-07-05 12:00:30 -04:00
Robbie Averill d94aa25ac7
Merge pull request #212 from sekjal/patch-1
Add support for GridfieldOrderableRows
2018-06-19 09:13:24 +12:00
Ian Walls dfbddb49a2
Add support for GridfieldOrderableRows
GridfieldExtensions' GridfieldOrderableRows is functionally quite similar (if not identical) to GridFieldSortableRows, and not all developers may want to support both modules.  This commit adds fallback support for sorting Document Sets using GridfieldOrderableRows if it is installed, and GridFieldSortableRows is not.

To test:
1. Uninstall GridFieldSortableRows (or start with an install without it)
2. Install GridFieldExtensions
3. Create/Open a Document Set
4. Make sure there are multiple files within.
5.  Drag/drop reorder without errors
2018-06-18 15:14:08 -04:00
Robbie Averill 4f5dddc17d
Merge pull request #211 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-18 10:48:14 +12:00
Dylan Wagstaff 6d36c67195 Add supported module badge to readme 2018-06-15 17:34:06 +12:00
Robbie Averill eb85a86296
Merge pull request #207 from silverstripe/revert-206-fix-page-history
Revert "fix page history: remove DMSSiteTreeExtension.getDocumentSets()"
2018-04-11 14:16:13 +12:00
Dylan Wagstaff 95d37b282d
Revert "fix page history: remove DMSSiteTreeExtension.getDocumentSets()" 2018-04-11 14:10:55 +12:00
Dylan Wagstaff 86bc9ec509 Merge up `2.1` into `master` 2018-04-11 14:08:13 +12:00
Dylan Wagstaff fad86bd93c
Merge pull request #206 from xini/fix-page-history
fix page history: remove DMSSiteTreeExtension.getDocumentSets()
2018-04-11 12:53:24 +12:00
Florian Thoma 1c226239f4 re-introduce getDocumentSets() as deprecated method to allow patch release 2018-04-11 10:34:52 +10:00
Florian Thoma 86e01cb6d1 fix template and tests 2018-03-01 12:31:44 +11:00
Florian Thoma 2c204bb0b6 remove DMSSiteTreeExtension.getDocumentSets() because it messes with the page history (readonly mode) 2018-03-01 12:15:44 +11:00
Robbie Averill eb96f4fd87 Merge branch '2.1' 2018-01-12 14:28:23 +13:00
Robbie Averill fb6bdf59c2
Merge pull request #205 from xini/patch-1
fix page edit link slug
2018-01-12 14:25:32 +13:00
Florian Thoma f36b439f8f fix page edit link slug 2018-01-12 13:48:14 +13:00
Robbie Averill b5f9602eb5 Merge branch '2.1' 2017-12-08 00:00:25 +13:00
Robbie Averill 35423c9aeb Merge remote-tracking branch 'origin/2.0' into 2.1 2017-12-07 23:59:44 +13:00
Robbie Averill 96bac3fdc9
Merge pull request #204 from creative-commoners/pulls/2.0/cast-number-as-int
FIX Cast shortcode document ID as int
2017-12-07 23:58:22 +13:00
Robbie Averill 046047e49f FIX Cast shortcode document ID as int 2017-12-07 16:41:52 +13:00
Robbie Averill ab9d361f98 Merge branch '2.1' 2017-12-07 15:46:32 +13:00
Robbie Averill b7a3a1df6e Merge branch '2.0' into 2.1 2017-12-07 15:45:42 +13:00
Damian Mooyman bcf2ac9757
Merge pull request #202 from creative-commoners/pulls/2.0/escape-file-execution
FIX Escape file path before loading file from filesystem
2017-12-07 14:59:36 +13:00
Robbie Averill 8efedf3158 FIX Escape file path before loading file from filesystem 2017-12-07 12:40:11 +13:00
Dylan Wagstaff 82a8a4b142
Merge pull request #201 from silverstripe/pulls/2.0/escape-panel-keys
FIX Ensure actions panel keys and values have possible HTML escaped
2017-12-06 14:01:31 +13:00
Dylan Wagstaff b7aaadf7bb
Merge pull request #200 from silverstripe/pulls/2.0/cast-dsid-as-ints
FIX Ensure document set IDs are cast to integers
2017-12-06 13:48:46 +13:00
Robbie Averill 0e84799f59
FIX Ensure actions panel keys and values have possible HTML escaped 2017-12-06 13:40:46 +13:00
Robbie Averill 854d1f9150
FIX Ensure document set IDs are cast to integers 2017-12-06 13:37:13 +13:00
Robbie Averill 1bceff58dd Merge branch '2.1' 2017-12-01 14:57:57 +13:00
Robbie Averill 99ee72e17f Merge branch '2.0' into 2.1 2017-12-01 14:55:41 +13:00
Robbie Averill eeca59f190 Remove obsolete branch alias 2017-12-01 14:54:14 +13:00
Robbie Averill 14b615f1e4
Merge pull request #199 from creative-commoners/pulls/2.1/rename-documentsets-tab-name
API Rename the DocumentSets tab name to remove spaces
2017-12-01 14:52:15 +13:00
Raissa North 0d27e614cd API Rename the DocumentSets tab name to remove spaces 2017-12-01 14:29:41 +13:00
Franco Springveldt 45092dfb16 FIX updated gridfieldextension version reference 2017-08-28 17:26:21 +12:00
Robbie Averill 2922e83dc1 Merge pull request #195 from qunabucom/master
Allow more sophisticated trackview as data extension
2017-08-16 22:20:32 +12:00
Mateusz Qunabu a61d294419 Allow more sophisticated trackview
adds `$this->extend('trackView');` into `trackView` function so developers can work on more sophisticated  track view of downloaded file, eg how had downloaded or when.
2017-08-16 11:23:59 +02:00
Robbie Averill ee9d2dfb58 Merge remote-tracking branch 'origin/2.0' 2017-08-07 14:21:11 +12:00
Franco Springveldt 16f0bef034 Merge pull request #194 from creative-commoners/pulls/2.0/fix-insert-document-toolbar
FIX Missing shortcode field and broken Javascript when inserting document
2017-08-07 13:34:21 +12:00
Robbie Averill 5efe1a4a87 FIX Add CSS bundle to link edit context document add form 2017-08-07 11:49:55 +12:00
Robbie Averill 6a67cddc38 FIX Missing shortcode field and broken Javascript when inserting document in TinyMCE 2017-08-07 11:21:35 +12:00
Robbie Averill 77f137fcda Use precise distro to include PHP 5.3 2017-07-31 22:16:47 +12:00
Robbie Averill b2c16477d1 Use precise distro to include PHP 5.3 2017-07-31 22:16:12 +12:00
Robbie Averill 97d55603d6 Merge remote-tracking branch 'origin/2.0' 2017-07-31 22:04:36 +12:00
Robbie Averill 6c5e70dc33 Merge pull request #191 from xini/patch-1
add dmsassets folder to file system sync exclusion, fixes #190
2017-07-31 22:02:23 +12:00
Robbie Averill b9b7be0417 Add test for exclusion of DMS folder name from Filesystem sync 2017-07-31 21:35:46 +12:00
Florian Thoma 9b31a7071f change folder extraction 2017-07-31 19:22:19 +10:00
Florian Thoma f41375e608 add dmsassets folder to file system sync exclusion, fixes #190 2017-07-31 18:14:04 +10:00
Robbie Averill f9f56b0ef2 Merge remote-tracking branch 'origin/2.0' 2017-07-27 14:19:32 +12:00
Franco Springveldt 1fd287b264 Merge pull request #189 from creative-commoners/pulls/2.0/fix-injectable-dmsuploadfield
FIX Ensure DMSUploadField called via injection is returned correctly
2017-07-27 14:11:08 +12:00
Robbie Averill d38e493925 Merge pull request #188 from xini/symbiote-change
update dependencies SSAU/symbiote
2017-07-27 14:04:54 +12:00
Robbie Averill ac5a01614b FIX Ensure DMSUploadField called via injection is returned correctly
Fixes an incompatibility with SelectUploadField in CWP
2017-07-27 13:36:50 +12:00
Florian Thoma 0338425cbc update dependencies SSAU/symbiote 2017-07-27 11:16:19 +10:00
Robbie Averill 06692ee4ae Merge remote-tracking branch 'origin/2.0' 2017-06-29 17:47:49 +12:00
sachajudd 9d117d6f49 Merge pull request #184 from creative-commoners/pulls/2.0/bug-can-edit-permissions-fix
FIX Removed duplicate permission check + removed SITETREEE_VIEW_ALL
2017-06-29 17:44:26 +12:00
Franco Springveldt e2eead8888 FIX Removed duplicate permission check + SITETREEE_VIEW_ALL permission from DMSDocument::canEdit 2017-06-29 17:30:45 +12:00
Robbie Averill a5ffec88bd Remove obsolete branch alias 2017-06-28 12:22:41 +12:00
Robbie Averill 31b3fff74e Update branch alias for 2.1.x-dev 2017-06-28 12:21:08 +12:00
Robbie Averill 1e8ac1fee8 Require stable taxonomy version 2017-06-28 10:09:21 +12:00
Daniel Hensby 6db701104c Merge pull request #182 from creative-commoners/pulls/2.0/fix-italics-for-userguide
DOCS Fix broken markdown italics for the userhelp guide
2017-06-20 15:19:33 +01:00
Daniel Hensby b4e5c29a04 Merge pull request #181 from silverstripe/pulls/2.0/userguide-reference
DOCS Add reference to userguide in main docs index
2017-06-20 15:19:06 +01:00
Sacha Judd 54b9b0b8ea DOCS Fix broken markdown italics for the userhelp guide 2017-06-20 16:42:34 +12:00
Robbie Averill 0d1e57eec6 DOCS Add reference to userguide in main docs index 2017-06-20 16:25:44 +12:00
Robbie Averill e5c7d1aa9a Merge pull request #180 from creative-commoners/pulls/2.0/add-userguide
DOCS Add userguide for DMS 2.0
2017-06-20 09:24:57 +12:00
Sacha Judd 8518739a13 DOCS Add userguide for DMS 2.0 2017-06-19 16:07:31 +12:00
Daniel Hensby d001a9a780 Merge pull request #179 from creative-commoners/pulls/2.0/fix-multi-version-edit-link
FIX Remove fragile logic looking for page edit link between SS versions
2017-06-15 14:09:25 +01:00
Robbie Averill 4ba3e3fa60 FIX Remove fragile logic looking for page edit link between SS versions 2017-06-15 14:31:10 +12:00
Franco Springveldt 4bdeb980d9 Merge pull request #177 from creative-commoners/pulls/2.0/fix-page-association-in-addcontroller
FIX Get page ID from document set and include it in $NewLink for add document buttons
2017-06-14 16:14:40 +12:00
Robbie Averill 9d5e028d10 FIX Ensure that redirection back to pages works for SS 3.6 2017-06-14 15:53:37 +12:00
Robbie Averill d57fc88bfa Merge pull request #168 from creative-commoners/issue/150
FIX document and documentset permissions
2017-06-14 11:59:17 +12:00
Franco Springveldt 79da01513e FIX document and documentset permissions 2017-06-14 11:18:09 +12:00
Robbie Averill d0c2558a1f FIX Get page ID from document set and include it in $NewLink for add document buttons
This helps the module to determine where to return a user to after they've uploaded a file, e.g. either back to a document set in a page context, back to a document set in the ModelAdmin context or back to the ModelAdmin for documents with no context.
2017-06-14 10:57:00 +12:00
sachajudd 4d83b95040 Merge pull request #176 from creative-commoners/pulls/2.0/remove-confirmation-message-on-delete
FIX Remove customised delete confirmation message, it taints the global scope
2017-06-13 16:02:35 +12:00
Robbie Averill a76b1d0256 FIX Remove customised delete confirmation message, it taints the global scope 2017-06-13 15:21:22 +12:00
sachajudd ed32ce949e Merge pull request #174 from creative-commoners/pulls/2.0/add-document-backlink
FIX Redirect back to documents tab after uploading a document
2017-06-13 11:33:53 +12:00
Robbie Averill 1f7b17a2d3 FIX Redirect back to documents tab after uploading a document
Unless you are within a document set or page context
2017-06-13 10:56:26 +12:00
sachajudd 98c20aa4dd Merge pull request #173 from creative-commoners/pulls/2.0/switch-coverage-version
Increase max allowed packets in mysql, add build for SS 3.6 explicitly
2017-06-13 10:56:12 +12:00
Robbie Averill e85e13123e Merge pull request #171 from creative-commoners/pulls/2.0/add-i18n-js-back
FIX Re-add i18n Javascript in documents modeladmin
2017-06-13 09:24:48 +12:00
Robbie Averill 72873608c4 Increase max allowed packets in mysql, add build for SS 3.6 explicitly 2017-06-12 18:50:24 +12:00
Sacha Judd c10109b49b FIX Re-add i18n Javascript in documents modeladmin 2017-06-12 16:24:23 +12:00
sachajudd b0de8b33e5 Merge pull request #167 from creative-commoners/pulls/2.0/fix-backlink-for-documentadmin
FIX Backlink for upload form redirects to document admin if no page is available
2017-06-08 13:43:37 +12:00
Robbie Averill 772198a7fc FIX Backlink for upload form redirects to document admin if no page is available 2017-06-08 12:26:53 +12:00
sachajudd c0dee2f1e5 Merge pull request #165 from creative-commoners/pulls/2.0/rename-getfilename
API Rename DMSDocument::getFileName to getFilename
2017-06-08 10:22:11 +12:00
Robbie Averill 14071eee3c API Rename DMSDocument::getFileName to getFilename 2017-06-08 09:54:44 +12:00
Robbie Averill 6b63f738d7 Merge pull request #164 from creative-commoners/pulls/2.0/suggest-modules
Suggest sortablegridfield in composer, loosen Travis constraint for it
2017-06-08 09:05:42 +12:00
Daniel Hensby bacc3db4ef Merge pull request #163 from creative-commoners/pulls/2.0/add-more-tests
Add some more tests
2017-06-07 21:39:18 +01:00
Robbie Averill edf2266326 Suggest sortablegridfield in composer, loosen Travis constraint for it 2017-06-07 21:40:51 +12:00
Robbie Averill 4c3206e200 Add tests for the document autocompleter and conditions in getter for page and document set 2017-06-07 17:00:25 +12:00
Robbie Averill 5d7479013e Add test class for DMSUploadField_ItemHandler 2017-06-07 16:39:44 +12:00
Robbie Averill 832eb14fad Add tests for returning link with content in shortcode handler and error handling 2017-06-07 16:29:19 +12:00
sachajudd 55ee260cba Merge pull request #146 from silverstripe/ux-fixes/issue7
NEW pages link to document sets
2017-06-07 16:17:33 +12:00
Franco Springveldt ce37b81c2b NEW pages link to document sets 2017-06-07 15:54:10 +12:00
sachajudd 0368293e74 Merge pull request #162 from silverstripe/issue/157
NEW info notices added to QueryBuilder
2017-06-07 11:00:36 +12:00
Franco Springveldt 5824c1b0eb NEW info notices added to QueryBuilder 2017-06-07 10:30:44 +12:00
Franco Springveldt 19eca581cc Merge pull request #161 from creative-commoners/pulls/2.0/travis-include-sortablegridfield
Add undefined/sortablegridfield to Travis test environment
2017-06-07 10:15:08 +12:00
sachajudd 5bea2fb5f1 Merge pull request #160 from creative-commoners/pulls/2.0/friendly-direction-labels
FIX Add friendly labels for ASC and DESC in query builder. Remove extension point in addQueryFields.
2017-06-07 09:36:40 +12:00
Robbie Averill f6fda3b241 Add undefined/sortablegridfield to Travis test environment 2017-06-06 16:57:27 +12:00
Robbie Averill f296c89dc0 FIX Add friendly labels for ASC and DESC in query builder. Remove extension point in addQueryFields.
Use updateCMSFields instead
2017-06-06 16:53:52 +12:00
sachajudd 84c7233b79 Merge pull request #159 from creative-commoners/pulls/2.0/fix-orderable-documents
FIX Ensure documents are drag-and-drop reorderable in document sets
2017-06-06 16:47:03 +12:00
sachajudd 8208822400 Merge pull request #149 from creative-commoners/pulls/2.0/add-existing-doc-sets
NEW Allow existing document sets to be linked to pages
2017-06-06 16:18:47 +12:00
Robbie Averill 58ac37a1e8 FIX Ensure documents are drag-and-drop reorderable in document sets 2017-06-06 16:14:14 +12:00
Robbie Averill 8a7fe273a6 NEW Allow existing document sets to be linked to pages 2017-06-06 16:03:52 +12:00
sachajudd 46aafb1df6 Merge pull request #153 from creative-commoners/pulls/2.0/remove-page-reference
FIX Remove "Page" literal field when editing a doc set in a page context. Simplify exception test.
2017-06-06 14:05:29 +12:00
Robbie Averill 7fb2662958 FIX Remove "Page" literal field when editing a doc set in a page context. Simplify exception test. 2017-06-06 12:08:06 +12:00
Robbie Averill d8f3685601 Merge pull request #145 from creative-commoners/pulls/2.0/add-set-button
FIX Add 'add document set' button and update label for Page
2017-06-06 11:05:08 +12:00
Sacha Judd cbd3a65fa9 FIX Add 'add document set' button and update label for Page 2017-06-03 08:24:14 +12:00
Robbie Averill d3cd151ebc Merge pull request #147 from silverstripe/ux-fixes/issue3
BUG title validation on DMSDocumentSet
2017-06-02 16:38:23 +12:00
Franco Springveldt 3d19b9fc75 FIX title validation on DMSDocumentSet 2017-06-02 16:05:10 +12:00
Franco Springveldt 886fd217f2 Merge pull request #141 from creative-commoners/pulls/2.0/doc-sets-modeladmin
NEW Add ModelAdmin interface for managing DMSDocumentSets
2017-05-31 10:34:32 +12:00
Robbie Averill e95182f8fc Merge pull request #144 from creative-commoners/pulls/2.0/js-refactor
Tidy up JS indentation and comments
2017-05-30 17:05:10 +12:00
Sacha Judd 9200d83b12 Tidy up JS indentation and comments 2017-05-30 16:46:45 +12:00
sachajudd a0ef0dc437 Merge pull request #142 from creative-commoners/pulls/2.0/fix-download-link-docs
DOCS Update "download link" example to not reference old functionality
2017-05-30 16:23:29 +12:00
Robbie Averill c7c4a356eb Merge pull request #143 from creative-commoners/pulls/2.0/getfieldsforfile-extensibility
NEW Reorder summary fields and make getFieldsForFile extensible
2017-05-30 14:24:47 +12:00
Sacha Judd aedc970cf4 NEW Reorder summary fields and make getFieldsForFile extensible 2017-05-30 13:51:56 +12:00
Robbie Averill d95b98d10d DOCS Update "download link" example to not reference old functionality 2017-05-30 12:05:29 +12:00
Robbie Averill cbc53fd7d2 NEW Add ModelAdmin interface for managing DMSDocumentSets 2017-05-29 10:15:21 +12:00
sachajudd 11819a1e16 Merge pull request #140 from creative-commoners/pulls/2.0/rename-json-field
FIX Rename JsonField to DMSJsonField to add a pseudo-namespace
2017-05-24 15:38:34 +12:00
sachajudd 5c8aeb0c58 Merge pull request #138 from creative-commoners/pulls/2.0/friendly-document-urls
NEW Add friendly URL segments for DMS documents
2017-05-24 15:20:31 +12:00
Robbie Averill 0ffd4d8584 FIX Rename JsonField to DMSJsonField to add a pseudo-namespace 2017-05-24 14:29:51 +12:00
Robbie Averill 80e36c3350 NEW Add friendly URL segments for DMS documents 2017-05-24 11:43:23 +12:00
sachajudd 2ddd20cd6c Merge pull request #133 from robbieaverill/pulls/2.0/css-refactor
FIX Refactor SCSS, convert to more BEM-like names, add admin icon
2017-05-22 16:56:30 +12:00
Robbie Averill 9b42effeb5 FIX Refactor SCSS, convert to more BEM-like names, add admin icon
* Remove "small" class from inline edit form for documents
* Swap relationeditor GridFieldConfig for record editor to ensure that document sets can be deleted from a page context rather than unlinked (natively)
* Fix to ensure that related documents can be autocompleted via partial matching on filename
* Add 2 space indentation rule to editorconfig for .js and .scss files
2017-05-22 15:20:16 +12:00
Franco Springveldt e8d46a90a5 Merge pull request #124 from robbieaverill/feature/tagging
API Remove DMSTag, replace with extension for taxonomy module
2017-05-18 22:49:44 +12:00
Robbie Averill 489a62abb2 API Remove DMSGridFieldDeleteAction, add query builder filter for taxonomy terms 2017-05-18 17:30:25 +12:00
Robbie Averill 17d97d65ce API Remove DMSTag, replace with optional extension for taxonomy module 2017-05-18 17:30:24 +12:00
sachajudd e4ab2b0861 Merge pull request #127 from robbieaverill/feature/change-storage-path
API Make storage path configurable. Fix instance singleton and remove static methods.
2017-05-18 17:20:59 +12:00
sachajudd 6afab52a09 Merge pull request #131 from robbieaverill/pulls/2.0/add-belongstoset-column
NEW Add column to document set GridField to show whether added manually or not
2017-05-18 15:42:10 +12:00
sachajudd 84abd5d837 Merge pull request #132 from robbieaverill/pulls/2.0/remove-autocompleter-for-document-sets
FIX Remove add existing autocompleter for document sets on a page
2017-05-18 15:36:14 +12:00
Robbie Averill 283f9fff7a API Make storage path configurable. Fix instance singleton and remove static methods 2017-05-18 15:14:09 +12:00
Robbie Averill 5cb92ecd7c FIX Remove add existing autocompleter for document sets on a page 2017-05-18 15:11:55 +12:00
Franco Springveldt 03fe480ca5 Merge pull request #128 from robbieaverill/feature/configurable-shortcode-handler-key
NEW Allow shortcode handler key to be configurable
2017-05-18 15:11:54 +12:00
Robbie Averill 84e1d3ff80 NEW Add column to document set GridField to show whether added manually or not 2017-05-18 15:07:12 +12:00
sachajudd fb989de12b Merge pull request #130 from robbieaverill/bugfix/permission-field-visibility
FIX Initial visibility of permission fields when editing a document
2017-05-18 14:32:26 +12:00
Robbie Averill 708721a5aa FIX Initial visibility of permission fields when editing a document 2017-05-18 12:47:22 +12:00
sachajudd 2039d72688 Merge pull request #129 from robbieaverill/bugfix/upload-document-backlink
FIX Ensure after uploading document you are redirected back to the document set
2017-05-18 12:04:20 +12:00
Robbie Averill 8d2f707f48 FIX Ensure after uploading document you are redirected back to the document set 2017-05-18 11:51:32 +12:00
Robbie Averill a6743b03f5 NEW Allow shortcode handler key to be configurable 2017-05-17 17:24:50 +12:00
Robbie Averill 961306ecc0 Merge pull request #119 from fspringveldt/feature/query-builder
Added Query Builder functionality to Document Sets
2017-05-16 14:25:14 +12:00
Franco Springveldt e08e276e89 Merge branch 'master' into feature/query-builder 2017-05-16 13:27:18 +12:00
Franco Springveldt 6b25237ec6 NEW Query Builder functionality added
This enhancement adds the ability to add documents to a document set based on a list of filters added from DMSDocument. Fixes #96
2017-05-16 13:26:01 +12:00
Franco Springveldt f506fb7247 Merge pull request #123 from sachajudd/feature/document-cover-image
NEW Add cover image to a document
2017-05-16 10:44:26 +12:00
Sacha Judd e1e5194c1c NEW Add cover image to a document 2017-05-15 17:17:10 +12:00
sachajudd 6b80f32832 Merge pull request #117 from robbieaverill/feature/webpack-scss
NEW Replace Compass with Webpack, update docs, remove unused @includes
2017-05-10 10:27:11 +12:00
sachajudd a59349e37f Merge pull request #113 from robbieaverill/feature/document-set-migration-guide
NEW Add migration task and documentation for 1.x to 2.x upgrade
2017-05-10 10:20:34 +12:00
sachajudd b42d446480 Merge pull request #118 from robbieaverill/bugfix/permissions-into-actions-tab
FIX Move permissions for documents into the "actions panel"
2017-05-10 10:09:45 +12:00
Robbie Averill a376b3480a FIX Move permissions for documents into the "actions panel" 2017-05-09 16:47:05 +12:00
Robbie Averill 513c29aa99 NEW Replace Compass with Webpack, update docs, remove unused @includes 2017-05-09 16:06:57 +12:00
Robbie Averill 5858511078 Bump minimum version requirements for framework and CMS to 3.5 2017-05-09 13:20:30 +12:00
Robbie Averill aafcc35f6c NEW Add migration task and documentation for 1.x to 2.x upgrade 2017-05-09 13:20:30 +12:00
sachajudd ed6f805574 Merge pull request #112 from robbieaverill/feature/document-sets
API Add document sets, remove *Page methods from DMSDocument
2017-05-09 13:19:24 +12:00
Robbie Averill 1c1b1d2aeb FIX Don't show GridField until set has been saved. Add more tests, fix typos in docs. 2017-05-09 13:06:37 +12:00
Robbie Averill 66abd22ee5 FIX Allow linking existing documents to document sets via add existing upload field 2017-05-09 13:04:29 +12:00
Robbie Averill b80aa645f8 MINOR Add more tests for classes in the "cms" code folder 2017-05-09 13:04:29 +12:00
Robbie Averill e4bc553521 API Add document sets, remove *Page methods from DMSDocument
* Add 2.0.0 changelog
* Update DMSInterface and DMSDocumentInterface removing *page and adding getDocumentSetsByPage to DMSInterface
* Update use documentation and update unit tests

This commit changes the relationship from Pages has_many Documents to Pages has_many DocumentSets which are many_many to Documents. The upload field has been upated to attach documents to a set instead of a page, the tests updated and the DMSInterface and DMSDocumentInterface updated to be less relevant to pages and more relevant to document sets.
2017-05-09 13:04:29 +12:00
sachajudd 9c5693dab0 Merge pull request #115 from robbieaverill/bugfix/documents-relate-to-themselves
FIX Do not allow documents to be related to themselves
2017-05-09 12:08:53 +12:00
Robbie Averill 2329ba4f40 FIX Do not allow documents to be related to themselves 2017-05-09 10:59:33 +12:00
sachajudd a6aa575501 Merge pull request #110 from fspringveldt/enhancement/92
Added ability to secure DMS assets by user group or role
2017-05-08 17:18:56 +12:00
Franco Springveldt 8682c7fca9 Added ability to secure DMS assets by user groups. 2017-05-08 16:12:46 +12:00
Franco Springveldt 38501542ee Merge pull request #111 from sachajudd/feature/modeladmin
NEW add DMSDocumentAdmin and new "add document" GridField component
2017-05-04 13:27:28 +12:00
Sacha Judd d29a115f7b NEW add DMSDocumentAdmin and new "add document" GridField component
The add new button wasn't modular so have added a GridField component which can be reused
2017-05-04 10:10:20 +12:00
Franco Springveldt 1f07069363 Merge pull request #105 from robbieaverill/bugfix/skip-dodgy-test
FIX Skip dodgy inconsistent unit test. Needs re-implementation.
2017-05-02 13:18:01 +12:00
Franco Springveldt b0ab198fc4 Merge pull request #107 from robbieaverill/bugfix/disable-codecov-comments
Add codecov YAML configuration to disable comments
2017-05-02 13:16:41 +12:00
Franco Springveldt bbe9e35d99 Merge pull request #106 from robbieaverill/feature/related-documents
NEW Relate documents to other documents and tidy up documentation
2017-05-02 13:08:29 +12:00
Robbie Averill 0b691b4f13 Add codecov YAML configuration to disable comments 2017-05-02 13:06:58 +12:00
Robbie Averill cef27febf6 DOCS Split readme documentation into docs/en files, add related documents information 2017-05-02 12:55:29 +12:00
Robbie Averill 9c0ec616d1 NEW Relate documents to other documents
* Modularise templates, add missing localisations, add related documents to template
* Add getRelatedDocuments as an extensible getter in DMSDocument
2017-05-02 12:38:55 +12:00
Robbie Averill e5f1b5c9db FIX Skip dodgy inconsistent unit test. Needs re-implementation. 2017-05-02 09:36:32 +12:00
Franco Springveldt 2e2112a3b5 Merge pull request #104 from robbieaverill/feature/add-license
Add BSD-3 Clause license file
2017-05-02 09:27:46 +12:00
Franco Springveldt 1d2defe0cc Merge pull request #103 from robbieaverill/feature/scrutinizer-and-codecov
Add Codecov integration and Codecov/Scrutinizer badges to readme
2017-05-02 09:26:31 +12:00
Robbie Averill 499ddebedf Update branch alias for 2.0.x-dev 2017-05-02 09:24:33 +12:00
Franco Springveldt bc130b3f1c Merge pull request #102 from robbieaverill/fix/psr-2
FIX Updates for coding standards, move second classes in files to their own files, fix comments
2017-05-02 09:14:27 +12:00
Robbie Averill ea587ee645 Add BSD-3 Clause license file 2017-05-01 17:02:44 +12:00
Robbie Averill 04188816c6 Add Codecov integration and Codecov/Scrutinizer badges to readme 2017-05-01 16:49:41 +12:00
Robbie Averill 39ce206b55 FIX Updates for coding standards, move second classes in files to their own files, fix comments 2017-05-01 15:56:27 +12:00
134 changed files with 10277 additions and 3267 deletions

1
.codecov.yml Normal file
View File

@ -0,0 +1 @@
comment: off

View File

@ -10,7 +10,7 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{*.yml,package.json}]
[{*.yml,package.json,*.scss,*.js}]
indent_size = 2
# The indent size used in the package.json file cannot be changed:

1
.gitattributes vendored
View File

@ -1,3 +1,4 @@
/docs export-ignore
/tests export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore

14
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Build Docs
on:
push:
branches:
- 'master'
paths:
- 'docs/en/userguide/**'
jobs:
build:
name: build-docs
runs-on: ubuntu-latest
steps:
- name: Run build hook
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_BUILD_HOOK }}

4
.gitignore vendored
View File

@ -1,2 +1,2 @@
.sass-cache
.DS_Store
node_modules/
npm-debug.log

View File

@ -1,37 +1,45 @@
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
sudo: false
# See https://github.com/silverstripe/silverstripe-travis-support for setup details
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
dist: trusty
env:
- DB=MYSQL CORE_RELEASE=3.2
before_install:
- sudo apt-get update
matrix:
include:
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.1
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.3
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.4
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.5
env: DB=MYSQL CORE_RELEASE=3.6
- php: 7.1
env: DB=MYSQL CORE_RELEASE=3.7
- php: 7.2
env: DB=MYSQL CORE_RELEASE=3.7 NPM_TEST=1
- php: 7.3
env: DB=MYSQL CORE_RELEASE=3.7 COVERAGE="--coverage-clover=coverage.xml"
before_script:
- echo -e "[server]\nmax_allowed_packet=64M" | sudo tee -a /etc/mysql/conf.d/dms.cnf
- sudo service mysql restart
- composer self-update || true
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
- cd ~/builds/ss
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/build/ss --require undefinedoffset/sortablegridfield:~0.6.9
- cd ~/build/ss
- composer install
- if [[ $NPM_TEST ]]; then npm install -g yarn && yarn install --network-concurrency 1; fi
script:
- vendor/bin/phpunit dms/tests
- vendor/bin/phpunit "$COVERAGE" dms/tests
- if [[ $NPM_TEST ]]; then cd dms; fi
- if [[ $NPM_TEST ]]; then yarn install --production=false; fi
- if [[ $NPM_TEST ]]; then yarn run build; fi
- if [[ $NPM_TEST ]]; then git diff-files --quiet -w --relative=client; fi
- if [[ $NPM_TEST ]]; then git diff --name-status --relative=client; fi
- if [[ $NPM_TEST ]]; then cd ..; fi
after_success:
- >
test "$COVERAGE" != ""
&& mv coverage.xml ~/build/$TRAVIS_REPO_SLUG
&& cd ~/build/$TRAVIS_REPO_SLUG
&& bash <(curl -s https://codecov.io/bash)

5
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,5 @@
# Contributing
Any open source product is only as good as the community behind it. You can participate by sharing code, ideas, or simply helping others. No matter what your skill level is, every contribution counts.
See our [high level overview](http://silverstripe.org/contributing-to-silverstripe) on silverstripe.org on how you can help out.

27
LICENSE.md Normal file
View File

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

122
README.md
View File

@ -1,131 +1,43 @@
# Document Management Module (DMS)
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-dms.png?branch=master)](https://travis-ci.org/silverstripe/silverstripe-dms)
[![Build status](https://travis-ci.org/silverstripe/silverstripe-dms.png?branch=master)](https://travis-ci.org/silverstripe/silverstripe-dms)
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
[![Code quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/?branch=master)
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-dms/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-dms)
![Helpful Robot](https://img.shields.io/badge/helpfulrobot-52-yellow.svg?style=flat)
## Overview
The module adds a new `DMSDocument` model which allows management
of large amounts of files, and their relations to pages.
In contrast to the `File` model built into SilverStripe core,
it aims to wrap storage and access concerns in a generic API,
which allows more fine-grained control over how the documents are
managed and exposed through the website.
The module adds a new `DMSDocument` model which allows management of large amounts of files, and their relations to
pages. In contrast to the `File` model built into SilverStripe core, it aims to wrap storage and access concerns in
a generic API. This allows more fine-grained control over how the documents are managed and exposed through the website.
Additionally, documents are stored and managed as part of a page instead of
away in a separate assets store.
Additionally, documents are stored and managed as part of a page instead of away in a separate assets store.
Read more about the DMS module in this [blog post on silverstripe.org](http://www.silverstripe.org/document-management-system-module)
Features:
## Features
* Relation of documents to pages
* Relation of documents to other documents
* Management and upload of documents within a page context in the CMS
* Metadata management through the powerful `GridField` and `UploadField` core APIs
* Configurable tags for documents
* Download via SilverStripe controller (rather than filesystem URLs)
* Access control based on PHP logic, and page relations
* Replacement of existing files
* Tagging via the [taxonomy module](https://github.com/silverstripe/silverstripe-taxonomy) if installed
## Documents on the Filesystem
## Documentation
While the DMS architecture allows for remote storage of files,
the default implementation (the `DMS` class) stores them locally.
Relations to pages and tags are persisted as many-many relationships
through the SilverStripe ORM.
For information on configuring and using this module, please see [the documentation section](docs/en/index.md).
File locations in this implementation are structured into
subfolders, in order to avoid exceeding filesystem limits.
The file name is a composite based on its database ID
and the original file name. The exact location shouldn't
be relied on by custom logic, but rather retrieved through
the API (`DMSDocument->getLink()`).
Example:
dms-assets/
0/
1234~myfile.pdf
1/
2345~myotherfile.pdf
### Requirements
## Requirements
* PHP 5.3 with the "fileinfo" module (or alternatively the "whereis" and "file" Unix commands)
* SilverStripe framework/CMS ^3.5
* [Taxonomy](https://github.com/silverstripe/silverstripe-taxonomy) ^1.2 (for tagging)
* (optional) [Pagination of Documents in the CMS](https://github.com/silverstripe-big-o/gridfieldpaginatorwithshowall)
* (optional) [Sorting of Documents in the CMS](https://github.com/silverstripe-big-o/SortableGridField)
* (optional) [Full text search of Documents](https://github.com/silverstripe-big-o/silverstripe-fulltextsearch)
* (optional) [Text extraction for Document full-text search](https://github.com/silverstripe-big-o/silverstripe-textextraction)
* (optional) [Tags](https://github.com/tubbs/silverstripe-dms-simple-tags)
### Configuration
The file location is set via the `DMS::$dmsFolder` static, and points to a location in the webroot.
### Usage
Add a simple include to any of your .ss templates to display the DMSDocuments associated with
the current page on the front-end.
<% include Documents %>
#### Create Documents
Create by relative path:
$dms = DMS::getDMSInstance();
$doc = $dms->storeDocument('assets/myfile.pdf');
Create from an existing `File` record:
$dms = DMS::getDMSInstance();
$file = File::get()->byID(99);
$doc = $dms->storeDocument($file);
Note: Both operations copy the existing file.
#### Download Documents
$dms = DMS::getDMSInstance();
$docs = $dms->getByTag('priority', 'important')->First();
$link = $doc->getLink();
// Set default download behavior ('open' or 'download'). 'download' is the system default
// Attempt to open the file in the browser
Config::inst()->update('DMSDocument', 'default_download_behaviour', 'open');
Or in you config.yml:
DMSDocument:
default_download_behaviour: open
#### Manage Page Relations
// Find documents by page
$dms = DMS::getDMSInstance();
$page = SiteTree::get()->filter('URLSegment', 'home')->First();
$docs = $dms->getByPage($page);
// Add documents to page
#### Manage Tags
// Find documents by tag
$dms = DMS::getDMSInstance();
$docs = $dms->getByTag('priority', 'important');
// Add tag to existing document
$doc = Document::get()->byID(99);
$doc->addTag('priority', 'low');
// Supports multiple values for tags
$doc->addTag('category', 'keyboard');
$doc->addTag('category', 'input device');
// Removing tags is abstracted as well
$doc->removeTag('category', 'keyboard');
$doc->removeTag('category', 'input device');
$doc->removeAllTags();
## Contributing

View File

@ -2,21 +2,30 @@
$config = Config::inst();
DMSSiteTreeExtension::show_documents_tab(); //show the Documents tab on all pages
DMSSiteTreeExtension::no_documents_tab(); //and don't exclude it from any pages
DMSDocumentAddController::add_allowed_extensions(); //add an array of additional allowed extensions
define('DMS_DIR', basename(__DIR__));
define('DMS_DIR', 'dms');
if (!file_exists(BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR)) {
user_error('DMS directory named incorrectly. Please install the DMS module into a folder named: ' . DMS_DIR);
}
if (!file_exists(BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR)) user_error("DMS directory named incorrectly. Please install the DMS module into a folder named: ".DMS_DIR);
// Ensure compatibility with PHP 7.2 ("object" is a reserved word),
// with SilverStripe 3.6 (using Object) and SilverStripe 3.7 (using SS_Object)
if (!class_exists('SS_Object')) class_alias('Object', 'SS_Object');
CMSMenu::remove_menu_item('DMSDocumentAddController');
ShortcodeParser::get('default')->register(
'dms_document_link', array('DMSShortcodeHandler', 'handle')
$config->get('DMS', 'shortcode_handler_key'),
array('DMSShortcodeHandler', 'handle')
);
if ($config->get('DMSDocument_versions', 'enable_versions')) {
//using the same db relations for the versioned documents, as for the actual documents
$config->update('DMSDocument_versions', 'db', $config->get('DMSDocument', 'db'));
//using the same db relations for the versioned documents, as for the actual documents
$config->update('DMSDocument_versions', 'db', $config->get('DMSDocument', 'db'));
}
// add dmsassets folder to file system sync exclusion
if (strpos($config->get('DMS', 'folder_name'), 'assets/') === 0) {
$folderName = substr($config->get('DMS', 'folder_name'), 7);
$config->update('Filesystem', 'sync_blacklisted_patterns', array("/^" . $folderName . "$/i",));
}

16
_config/config.yml Normal file
View File

@ -0,0 +1,16 @@
---
Name: dmsconfig
---
SiteTree:
extensions:
- DMSSiteTreeExtension
# Whether to show the document sets tab in the CMS for the page type this extension is applied to
documents_enabled: true
HtmlEditorField_Toolbar:
extensions:
- DocumentHtmlEditorFieldToolbar
# Ensure that if the parent UploadField is overloaded with the injector, that the DMSUploadField is returned
Injector:
DMSUploadField: DMSUploadField

View File

@ -5,11 +5,6 @@ After: framework/routes#coreroutes
Director:
rules:
'dmsdocument/$ID' : 'DMSDocument_Controller'
SiteTree:
extensions:
- DMSSiteTreeExtension
HtmlEditorField_Toolbar:
extensions:
- DocumentHtmlEditorFieldToolbar
DMSDocument_versions:
enable_versions: true

16
_config/querybuilder.yml Normal file
View File

@ -0,0 +1,16 @@
DMSDocument:
searchable_fields:
Title:
title: Document title matches
Description:
title: Document summary matches
CreatedByID:
title: Document created by
field: ListboxField
filter: ExactMatchFilter
LastEditedByID:
title: Document last changed by
field: ListboxField
filter: ExactMatchFilter
Filename:
title: File name

25
_config/taxonomy.yml Normal file
View File

@ -0,0 +1,25 @@
---
Name: dmstaxonomy
Only:
moduleexists: taxonomy
---
TaxonomyType:
extensions:
- DMSTaxonomyTypeExtension
DMSDocument:
extensions:
- DMSDocumentTaxonomyExtension
# Add query builder filter for tags
searchable_fields:
Tags.ID:
title: Document has tags
field: ListboxField
filter: ExactMatchFilter
DMSTaxonomyTypeExtension:
# Referenced to filter taxonomy terms for DMS Documents.
# To change, update the default_records array too.
default_record_name: Document
default_records:
- Document

View File

@ -1,42 +1,91 @@
<?php
class DMS implements DMSInterface
class DMS extends SS_Object implements DMSInterface
{
/**
* Folder to store the documents in
*
* @config
* @var string
*/
private static $folder_name = 'assets/_dmsassets';
public static $dmsFolder = 'dms-assets'; //folder to store the documents in
/**
* How many documents to store in a single folder. The square of this number is the maximum number of documents.
*
* The number should be a multiple of 10
*
* @config
* @var int
*/
private static $folder_size = 1000;
//How many documents to store in a single folder. The square of this number is the maximum number of documents.
//The number should be a multiple of 10
public static $dmsFolderSize = 1000;
/**
* Singleton instance of a DMSInterface
*
* @var DMSInterface
*/
private static $instance;
/**
* The shortcode handler key. Can be changed by user code.
*
* @config
* @var string
*/
private static $shortcode_handler_key = 'dms_document_link';
/**
* Factory method that returns an instance of the DMS. This could be any class that implements the DMSInterface.
* @static
*
* @return DMSInterface An instance of the Document Management System
*/
public static function inst()
{
$dmsPath = self::get_dms_path();
if (!self::$instance) {
self::$instance = new static();
$dms = new DMS();
if (!is_dir($dmsPath)) {
self::create_storage_folder($dmsPath);
}
$dmsPath = self::$instance->getStoragePath();
if (!file_exists($dmsPath . DIRECTORY_SEPARATOR . '.htaccess')) {
//restrict access to the storage folder
copy(BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . '.htaccess', $dmsPath . DIRECTORY_SEPARATOR . '.htaccess');
copy(BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'web.config', $dmsPath . DIRECTORY_SEPARATOR . 'web.config');
if (!is_dir($dmsPath)) {
self::$instance->createStorageFolder($dmsPath);
}
if (!file_exists($dmsPath . DIRECTORY_SEPARATOR . '.htaccess')) {
// Restrict access to the storage folder
copy(
BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR . DIRECTORY_SEPARATOR
. 'resources' . DIRECTORY_SEPARATOR . '.htaccess',
$dmsPath . DIRECTORY_SEPARATOR . '.htaccess'
);
copy(
BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR . DIRECTORY_SEPARATOR
. 'resources' . DIRECTORY_SEPARATOR . 'web.config',
$dmsPath . DIRECTORY_SEPARATOR . 'web.config'
);
}
}
return $dms;
return self::$instance;
}
public static function get_dms_path()
/**
* Get the storage path for DMS documents
*
* @return string
*/
public function getStoragePath()
{
return BASE_PATH . DIRECTORY_SEPARATOR . self::$dmsFolder;
return BASE_PATH . DIRECTORY_SEPARATOR . $this->config()->get('folder_name');
}
public static function transform_file_to_file_path($file)
/**
* Gets a file path from either a File or a string
*
* @param string|File $file
* @return string
* @throws FileNotFoundException If an unexpected value was provided, or the filename was null
*/
public function transformFileToFilePath($file)
{
//confirm we have a file
$filePath = null;
@ -56,35 +105,22 @@ class DMS implements DMSInterface
/**
* Takes a File object or a String (path to a file) and copies it into the DMS. The original file remains unchanged.
* When storing a document, sets the fields on the File has "tag" metadata.
* @param $file File object, or String that is path to a file to store, e.g. "assets/documents/industry/supplied-v1-0.pdf"
* @param File|string $file File object, or String that is path to a file to store,
* e.g. "assets/documents/industry/supplied-v1-0.pdf"
* @return DMSDocument
*/
public function storeDocument($file)
{
$filePath = self::transform_file_to_file_path($file);
//create a new document and get its ID
$doc = new DMSDocument();
$filePath = $this->transformFileToFilePath($file);
// Create a new document and get its ID
$doc = DMSDocument::create();
$doc->write();
$doc->storeDocument($filePath);
return $doc;
}
/**
*
* Returns a number of Document objects based on the a search by tags. You can search by category alone,
* by tag value alone, or by both. I.e: getByTag("fruits",null); getByTag(null,"banana"); getByTag("fruits","banana")
* @param null $category The metadata category to search for
* @param null $value The metadata value to search for
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DocumentInterface
*/
public function getByTag($category = null, $value = null, $showEmbargoed = false)
{
// TODO: Implement getByTag() method.
}
/**
* Returns a number of Document objects that match a full-text search of the Documents and their contents
* (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module)
@ -97,34 +133,58 @@ class DMS implements DMSInterface
// TODO: Implement getByFullTextSearch() method.
}
/**
* Returns a list of Document objects associated with a Page
* @param $page SiteTree to fetch the associated Documents from
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DataList Document list associated with the Page
*/
public function getByPage($page, $showEmbargoed = false)
public function getByPage(SiteTree $page, $showEmbargoed = false)
{
// TODO: Implement getByPage() method.
/** @var ArrayList $documents */
$documents = $page->getAllDocuments();
if (!$showEmbargoed) {
foreach ($documents as $document) {
if ($document->isEmbargoed()) {
$documents->remove($document);
}
}
}
return $documents;
}
public function getDocumentSetsByPage(SiteTree $page)
{
return $page->getDocumentSets();
}
/**
* Creates a storage folder for the given path
* @param $path Path to create a folder for
*
* @param string $path Path to create a folder for
* @return $this
*/
public static function create_storage_folder($path)
public function createStorageFolder($path)
{
if (!is_dir($path)) {
mkdir($path, 0777);
mkdir($path, 0777, true);
}
return $this;
}
/**
* Calculates the storage path from a database DMSDocument ID
*
* @return int
*/
public static function get_storage_folder($id)
public function getStorageFolder($id)
{
$folderName = intval($id / self::$dmsFolderSize);
return $folderName;
return intval($id / self::config()->get('folder_size'));
}
/**
* Get the shortcode handler key
*
* @return string
*/
public function getShortcodeHandlerKey()
{
return (string) Config::inst()->get('DMS', 'shortcode_handler_key');
}
}

View File

@ -8,26 +8,26 @@
*/
class DMSShortcodeHandler
{
public static function handle(
$arguments, $content, ShortcodeParser $parser, $tag, array $extra = array()
) {
public static function handle($arguments, $content, ShortcodeParser $parser, $tag, array $extra = array())
{
if (!empty($arguments['id'])) {
$document = DMSDocument::get()->byID($arguments['id']);
if ($document && !$document->isHidden()) {
if ($content) {
return sprintf(
'<a href="%s">%s</a>', $document->Link(), $parser->parse($content)
'<a href="%s">%s</a>',
$document->Link(),
$parser->parse($content)
);
} else {
if (isset($extra['element'])) {
$extra['element']->setAttribute('data-ext', $document->getExtension());
$extra['element']->setAttribute('data-size', $document->getFileSizeFormatted());
}
return $document->Link();
}
if (isset($extra['element'])) {
$extra['element']->setAttribute('data-ext', $document->getExtension());
$extra['element']->setAttribute('data-size', $document->getFileSizeFormatted());
}
return $document->Link();
}
}

View File

@ -3,10 +3,8 @@
/**
* @package dms
*/
class DMSDocumentAddController extends LeftAndMain
{
private static $url_segment = 'pages/adddocument';
private static $url_priority = 60;
private static $required_permission_codes = 'CMS_ACCESS_AssetAdmin';
@ -14,7 +12,13 @@ class DMSDocumentAddController extends LeftAndMain
private static $tree_class = 'SiteTree';
private static $session_namespace = 'CMSMain';
public static $allowed_extensions = array();
/**
* Allowed file upload extensions, will be merged with `$allowed_extensions` from {@link File}
*
* @config
* @var array
*/
private static $allowed_extensions = array();
private static $allowed_actions = array(
'getEditForm',
@ -23,48 +27,42 @@ class DMSDocumentAddController extends LeftAndMain
'documentlist'
);
/**
* Add an array of additional allowed extensions
* @static
* @param $array
*/
public static function add_allowed_extensions($array = null)
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$allowed_extensions = $array;
} else {
self::$allowed_extensions = array($array);
}
}
/**
* Custom currentPage() method to handle opening the 'root' folder
*
* @return
* @return SiteTree
*/
public function currentPage()
{
$id = $this->currentPageID();
if ($id && is_numeric($id) && $id > 0) {
return Versioned::get_by_stage('SiteTree', 'Stage', sprintf(
'ID = %s', (int) $id
))->first();
} else {
// ID is either '0' or 'root'
return singleton('SiteTree');
if ($id === 0) {
return SiteTree::singleton();
}
return parent::currentPage();
}
/**
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder). Otherwise the page ID
* is passed in from the {@link DMSGridFieldAddNewButton}.
*
* @return int
*/
public function currentPageID()
{
return ($result = parent::currentPageID()) === null ? 0 : $result;
return (int) $this->getRequest()->getVar('page_id');
}
/**
* Get the current document set, if a document set ID was provided
*
* @return DMSDocumentSet
*/
public function getCurrentDocumentSet()
{
if ($id = $this->getRequest()->getVar('dsid')) {
return DMSDocumentSet::get()->byId($id);
}
return singleton('DMSDocumentSet');
}
/**
@ -75,9 +73,13 @@ class DMSDocumentAddController extends LeftAndMain
{
Requirements::javascript(FRAMEWORK_DIR . '/javascript/AssetUploadField.js');
Requirements::css(FRAMEWORK_DIR . '/css/AssetUploadField.css');
Requirements::css(DMS_DIR.'/css/DMSMainCMS.css');
Requirements::css(DMS_DIR . '/dist/css/cmsbundle.css');
/** @var SiteTree $page */
$page = $this->currentPage();
/** @var DMSDocumentSet $documentSet */
$documentSet = $this->getCurrentDocumentSet();
$uploadField = DMSUploadField::create('AssetUploadField', '');
$uploadField->setConfig('previewMaxWidth', 40);
$uploadField->setConfig('previewMaxHeight', 30);
@ -87,27 +89,32 @@ class DMSDocumentAddController extends LeftAndMain
$uploadField->addExtraClass('ss-assetuploadfield');
$uploadField->removeExtraClass('ss-uploadfield');
$uploadField->setTemplate('AssetUploadField');
$uploadField->setRecord($page);
$uploadField->setRecord($documentSet);
$uploadField->getValidator()->setAllowedExtensions(array_filter(array_merge(Config::inst()->get('File', 'allowed_extensions'), self::$allowed_extensions)));
$uploadField->getValidator()->setAllowedExtensions($this->getAllowedExtensions());
$exts = $uploadField->getValidator()->getAllowedExtensions();
asort($exts);
$backlink = $this->Backlink();
$done = "
<a class=\"ss-ui-button ss-ui-action-constructive cms-panel-link ui-corner-all\" href=\"".$backlink."\">
Done!
<a class=\"ss-ui-button ss-ui-action-constructive cms-panel-link ui-corner-all\" href=\"" . $backlink . "\">
" . _t('UploadField.DONE', 'DONE') . "
</a>";
$addExistingField = new DMSDocumentAddExistingField('AddExisting', 'Add Existing');
$addExistingField->setRecord($page);
$addExistingField = new DMSDocumentAddExistingField(
'AddExisting',
_t('DMSDocumentAddExistingField.ADDEXISTING', 'Add Existing')
);
$addExistingField->setRecord($documentSet);
$form = new Form(
$this,
'getEditForm',
new FieldList(
new TabSet('Main',
new Tab('From your computer',
new HiddenField('ID', false, $page->ID),
new TabSet(
_t('DMSDocumentAddController.MAINTAB', 'Main'),
new Tab(
_t('UploadField.FROMCOMPUTER', 'From your computer'),
$uploadField,
new LiteralField(
'AllowedExtensions',
@ -118,7 +125,8 @@ class DMSDocumentAddController extends LeftAndMain
)
)
),
new Tab('From the CMS',
new Tab(
_t('UploadField.FROMCMS', 'From the CMS'),
$addExistingField
)
)
@ -131,17 +139,8 @@ class DMSDocumentAddController extends LeftAndMain
$form->Backlink = $backlink;
// Don't use AssetAdmin_EditForm, as it assumes a different panel structure
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
/*$form->Fields()->push(
new LiteralField(
'BackLink',
sprintf(
'<a href="%s" class="backlink ss-ui-button cms-panel-link" data-icon="back">%s</a>',
Controller::join_links(singleton('AssetAdmin')->Link('show'), $folder ? $folder->ID : 0),
_t('AssetAdmin.BackToFolder', 'Back to folder')
)
)
);*/
//$form->loadDataFrom($folder);
$form->Fields()->push(HiddenField::create('ID', false, $documentSet->ID));
$form->Fields()->push(HiddenField::create('DSID', false, $documentSet->ID));
return $form;
}
@ -165,27 +164,69 @@ class DMSDocumentAddController extends LeftAndMain
}
$items->push(new ArrayData(array(
'Title' => 'Add Document',
'Title' => _t('DMSDocumentSet.ADDDOCUMENTBUTTON', 'Add Document'),
'Link' => $this->Link()
)));
return $items;
}
/**
* Returns the link to be used to return the user after uploading a document. Scenarios:
*
* 1) Page context: page ID and document set ID provided, redirect back to the page and document set
* 2) Document set context: no page ID, document set ID provided, redirect back to document set in ModelAdmin
* 3) Document context: no page ID and no document set ID provided, redirect back to documents in ModelAdmin
*
* @return string
*/
public function Backlink()
{
$pageID = $this->currentPageID();
return Controller::join_links(singleton('CMSPagesController')->Link(), 'edit/show', $pageID);
if (!$this->getRequest()->getVar('dsid') || !$this->currentPageID()) {
$modelAdmin = new DMSDocumentAdmin;
$modelAdmin->init();
if ($this->getRequest()->getVar('dsid')) {
return Controller::join_links(
$modelAdmin->Link('DMSDocumentSet'),
'EditForm/field/DMSDocumentSet/item',
(int) $this->getRequest()->getVar('dsid'),
'edit'
);
}
return $modelAdmin->Link();
}
return $this->getPageEditLink($this->currentPageID(), (int) $this->getRequest()->getVar('dsid'));
}
/**
* Return a link to edit a page, deep linking into the document set given
*
* @param int $pageId
* @param int $documentSetId
* @return string
*/
protected function getPageEditLink($pageId, $documentSetId)
{
return Controller::join_links(
CMSPageEditController::singleton()->getEditForm($pageId)->FormAction(),
'field/DocumentSets/item',
(int) $documentSetId
);
}
public function documentautocomplete()
{
$term = (isset($_GET['term'])) ? $_GET['term'] : '';
$term_sql = Convert::raw2sql($term);
$term = (string) $this->getRequest()->getVar('term');
$termSql = Convert::raw2sql($term);
$data = DMSDocument::get()
->where("(\"ID\" LIKE '%".$term_sql."%' OR \"Filename\" LIKE '%".$term_sql."%' OR \"Title\" LIKE '%".$term_sql."%')")
->sort('ID ASC')
->limit(20);
->where(
'("ID" LIKE \'%' . $termSql . '%\' OR "Filename" LIKE \'%' . $termSql . '%\''
. ' OR "Title" LIKE \'%' . $termSql . '%\')'
)
->sort('ID ASC')
->limit(20);
$return = array();
foreach ($data as $doc) {
@ -195,49 +236,60 @@ class DMSDocumentAddController extends LeftAndMain
);
}
return json_encode($return);
return Convert::raw2json($return);
}
/**
* Link an existing document to the given document set ID
* @return string JSON
*/
public function linkdocument()
{
$return = array('error' => _t('UploadField.FIELDNOTSET', 'Could not add document to page'));
$documentSet = $this->getCurrentDocumentSet();
if (!empty($documentSet)) {
$document = DMSDocument::get()->byId($this->getRequest()->getVar('documentID'));
$documentSet->Documents()->add($document);
$page = $this->currentPage();
if (!empty($page)) {
$document = DataObject::get_by_id('DMSDocument', (int) $_GET['documentID']);
$document->addPage($page);
$buttonText = '<button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all" title="Edit this document" data-icon="pencil">'.
'Edit<span class="toggle-details"><span class="toggle-details-icon"></span></span></button>';
$buttonText = '<button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all"'
. ' title="' . _t('DMSDocument.EDITDOCUMENT', 'Edit this document') . '" data-icon="pencil">'
. _t('DMSDocument.EDIT', 'Edit') . '<span class="toggle-details">'
. '<span class="toggle-details-icon"></span></span></button>';
// Collect all output data.
$return = array(
'id' => $document->ID,
'name' => $document->getTitle(),
'thumbnail_url' => $document->Icon($document->getExtension()),
'edit_url' => $this->getEditForm()->Fields()->fieldByName('Main.From your computer.AssetUploadField')->getItemHandler($document->ID)->EditLink(),
'edit_url' => $this->getEditForm()->Fields()->fieldByName('Main.From your computer.AssetUploadField')
->getItemHandler($document->ID)->EditLink(),
'size' => $document->getFileSizeFormatted(),
'buttons' => $buttonText,
'showeditform' => true
);
}
return json_encode($return);
return Convert::raw2json($return);
}
/**
* Returns HTML representing a list of documents that are associated with the given page ID, across all document
* sets.
*
* @return string HTML
*/
public function documentlist()
{
if (!isset($_GET['pageID'])) {
if (!$this->getRequest()->getVar('pageID')) {
return $this->httpError(400);
}
$page = SiteTree::get()->byId($_GET['pageID']);
$page = SiteTree::get()->byId($this->getRequest()->getVar('pageID'));
if ($page && $page->Documents()->count() > 0) {
if ($page && $page->getAllDocuments()->count() > 0) {
$list = '<ul>';
foreach ($page->Documents() as $document) {
foreach ($page->getAllDocuments() as $document) {
$list .= sprintf(
'<li><a class="add-document" data-document-id="%s">%s</a></li>',
$document->ID,
@ -249,9 +301,51 @@ class DMSDocumentAddController extends LeftAndMain
return $list;
}
return sprintf('<p>%s</p>',
return sprintf(
'<p>%s</p>',
_t('DMSDocumentAddController.NODOCUMENTS', 'There are no documents attached to the selected page.')
);
}
/**
* Get an array of allowed file upload extensions, merged with {@link File} and extra configuration from this
* class
*
* @return array
*/
public function getAllowedExtensions()
{
return array_filter(
array_merge(
(array) Config::inst()->get('File', 'allowed_extensions'),
(array) $this->config()->get('allowed_extensions')
)
);
}
/**
* Overrides the parent method to allow users with access to DMS admin to access this controller
*
* @param Member $member
* @return bool
*/
public function canView($member = null)
{
if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
$member = Member::currentUser();
}
if ($member &&
Permission::checkMember(
$member,
array(
'CMS_ACCESS_DMSDocumentAdmin',
)
)
) {
return true;
}
return parent::canView($member);
}
}

View File

@ -2,7 +2,6 @@
class DMSDocumentAddExistingField extends CompositeField
{
public $useFieldContext = true;
public function __construct($name, $title = null)
@ -10,7 +9,15 @@ class DMSDocumentAddExistingField extends CompositeField
$this->name = $name;
$this->title = ($title === null) ? $name : $title;
parent::__construct(new TreeDropdownField('PageSelector', 'Add from another page', 'SiteTree', 'ID', 'TitleWithNumberOfDocuments'));
parent::__construct(
new TreeDropdownField(
'PageSelector',
'Add from another page',
'SiteTree',
'ID',
'TitleWithNumberOfDocuments'
)
);
}
/**
@ -23,7 +30,8 @@ class DMSDocumentAddExistingField extends CompositeField
return $this;
}
/**
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it will use Form->getRecord() or Form->Controller()->data()
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it
* will use Form->getRecord() or Form->Controller()->data()
* @return DataObject
*/
public function getRecord()
@ -46,8 +54,9 @@ class DMSDocumentAddExistingField extends CompositeField
public function Field($properties = array())
{
Requirements::javascript(DMS_DIR.'/javascript/DMSDocumentAddExistingField.js');
Requirements::javascript(DMS_DIR."/javascript/DocumentHtmlEditorFieldToolbar.js");
Requirements::javascript(DMS_DIR . '/javascript/DMSDocumentAddExistingField.js');
Requirements::javascript(DMS_DIR . '/javascript/DocumentHtmlEditorFieldToolbar.js');
Requirements::css(DMS_DIR . '/dist/css/cmsbundle.css');
return $this->renderWith('DMSDocumentAddExistingField');
}
@ -55,9 +64,12 @@ class DMSDocumentAddExistingField extends CompositeField
/**
* Sets or unsets the use of the "field" class in the template. The "field" class adds Javascript behaviour
* that causes unwelcome hiding side-effects when this Field is used within the link editor pop-up
*
* @return $this
*/
public function setUseFieldClass($use = false)
{
$this->useFieldContext = $use;
return $this;
}
}

View File

@ -0,0 +1,79 @@
<?php
class DMSDocumentAdmin extends ModelAdmin
{
private static $managed_models = array(
'DMSDocument',
'DMSDocumentSet'
);
private static $url_segment = 'documents';
private static $menu_title = 'Documents';
private static $menu_icon = 'dms/images/app_icons/drawer.png';
public function init()
{
parent::init();
Requirements::javascript(DMS_DIR . '/javascript/DMSGridField.js');
}
/**
* Remove the default "add" button and replace it with a customised version for DMS
*
* @return CMSForm
*/
public function getEditForm($id = null, $fields = null)
{
/** @var CMSForm $form */
$form = parent::getEditForm($id, $fields);
$gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass));
return $this->modifyGridField($form, $gridField);
}
/**
* If the GridField is for DMSDocument then add a custom "add" button. If it's for DMSDocumentSet then
* update the display fields to include some extra columns that are only for this ModelAdmin, so cannot
* be added directly to the model's display fields.
*
* @param CMSForm $form
* @param GridField $gridField
* @return CMSForm
*/
protected function modifyGridField(CMSForm $form, GridField $gridField)
{
$gridFieldConfig = $gridField->getConfig();
$gridFieldConfig->removeComponentsByType('GridFieldEditButton');
$gridFieldConfig->addComponent(new DMSGridFieldEditButton(), 'GridFieldDeleteAction');
if ($this->modelClass === 'DMSDocument') {
$gridFieldConfig->removeComponentsByType('GridFieldAddNewButton');
$gridFieldConfig->addComponent(
new DMSGridFieldAddNewButton('buttons-before-left'),
'GridFieldExportButton'
);
} elseif ($this->modelClass === 'DMSDocumentSet') {
$dataColumns = $gridFieldConfig->getComponentByType('GridFieldDataColumns');
$fields = $dataColumns->getDisplayFields($gridField);
$fields = array('Title' => 'Title', 'Page.Title' => 'Page') + $fields;
$dataColumns->setDisplayFields($fields)
->setFieldFormatting(
array(
'Page.Title' => function ($value, $item) {
// Link a page click directly to the Document Set on the actual page
if ($page = SiteTree::get()->byID($item->PageID)) {
return sprintf(
"<a class='dms-doc-sets-link' href='%s/#Root_DocumentSets%s'>$value</a>",
$page->CMSEditLink(),
$page->DocumentSets()->count()
);
}
}
)
);
}
return $form;
}
}

View File

@ -0,0 +1,76 @@
<?php
class DMSGridFieldAddNewButton extends GridFieldAddNewButton implements GridField_HTMLProvider
{
/**
* The document set ID that the document should be attached to
*
* @var int
*/
protected $documentSetId;
/**
* Overriding the parent method to change the template that the DMS add button will be rendered with
*
* @param GridField $gridField
* @return array
*/
public function getHTMLFragments($gridField)
{
$singleton = singleton($gridField->getModelClass());
if (!$singleton->canCreate()) {
return array();
}
if (!$this->buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component
$objectName = $singleton->i18n_singular_name();
$this->buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
}
$link = singleton('DMSDocumentAddController')->Link();
if ($this->getDocumentSetId()) {
$link = Controller::join_links($link, '?dsid=' . $this->getDocumentSetId());
// Look for an associated page, but only share it if we're editing in a page context
$set = DMSDocumentSet::get()->byId($this->getDocumentSetId());
if ($set && $set->exists() && $set->Page()->exists()
&& Controller::curr() instanceof CMSPageEditController
) {
$link = Controller::join_links($link, '?page_id=' . $set->Page()->ID);
}
}
$data = new ArrayData(array(
'NewLink' => $link,
'ButtonName' => $this->buttonName,
));
return array(
$this->targetFragment => $data->renderWith('DMSGridFieldAddNewButton'),
);
}
/**
* Set the document set ID that this document should be attached to
*
* @param int $id
* @return $this
*/
public function setDocumentSetId($id)
{
$this->documentSetId = $id;
return $this;
}
/**
* Get the document set ID that this document should be attached to
*
* @return int
*/
public function getDocumentSetId()
{
return $this->documentSetId;
}
}

View File

@ -1,96 +0,0 @@
<?php
/**
* This class is a {@link GridField} component that adds a delete a DMS document.
*
* This component also supports unlinking a relation instead of deleting the object. By default it unlinks, but if
* this is the last reference to a specific document, it warns the user that continuing with the operation will
* delete the document completely.
*
* <code>
* $action = new GridFieldDeleteAction(); // delete objects permanently
* </code>
*
* @package dms
* @subpackage cms
*/
class DMSGridFieldDeleteAction extends GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider
{
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName)
{
if ($this->removeRelation) {
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false, "unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
} else {
if (!$record->canDelete()) {
return;
}
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "Delete"))
->setAttribute('data-icon', 'cross-circle')
->setDescription(_t('GridAction.DELETE_DESCRIPTION', 'Delete'));
}
//add a class to the field to if it is the last gridfield in the list
$numberOfRelations = $record->Pages()->Count();
$field->addExtraClass('dms-delete') //add a new class for custom JS to handle the delete action
->setAttribute('data-pages-count', $numberOfRelations) //add the number of pages attached to this field as a data-attribute
->removeExtraClass('gridfield-button-delete'); //remove the base gridfield behaviour
//set a class telling JS what kind of warning to display when clicking the delete button
if ($numberOfRelations > 1) {
$field->addExtraClass('dms-delete-link-only');
} else {
$field->addExtraClass('dms-delete-last-warning');
}
//set a class to show if the document is hidden
if ($record->isHidden()) {
$field->addExtraClass('dms-document-hidden');
}
return $field->Field();
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
if ($actionName == 'deleterecord' || $actionName == 'unlinkrelation') {
$item = $gridField->getList()->byID($arguments['RecordID']);
if (!$item) {
return;
}
if ($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(_t('GridFieldAction_Delete.DeletePermissionsFailure', "No delete permissions"), 0);
}
$delete = false;
if ($item->Pages()->Count() <= 1) {
$delete = true;
}
$gridField->getList()->remove($item); //remove the relation
if ($delete) {
$item->delete();
} //delete the DMSDocument
}
}
}

View File

@ -13,7 +13,7 @@ class DMSGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
//add a data attribute specifying how many pages this document is referenced on
if ($record = $this->record) {
$numberOfPageRelations = $record->Pages()->Count();
$numberOfPageRelations = $record->getRelatedPages()->count();
$relations = new ShortCodeRelationFinder();
$numberOfInlineRelations = $relations->findPageCount($record->ID);

View File

@ -0,0 +1,26 @@
<?php
class DMSGridFieldEditButton extends GridFieldEditButton implements GridField_ColumnProvider
{
/**
* Overriding the parent method to change the template that the DMS edit button will be rendered with based on
* whether or not the user has edit permissions.
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
*
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName)
{
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
$template = $record->canEdit() ? 'GridFieldEditButton' : 'GridFieldViewButton';
return $data->renderWith($template);
}
}

View File

@ -18,17 +18,12 @@ class DMSUploadField extends UploadField
"upload",
);
/**
* The temporary folder name to store files in during upload
* @var string
*/
protected $folderName = 'DMSTemporaryUploads';
public function __construct($name, $title = null, SS_List $items = null)
{
parent::__construct($name, $title, $items);
//set default DMS replace template to false
$this->setConfig('useDMSReplaceTemplate', 0);
}
/**
* Override the default behaviour of the UploadField and take the uploaded file (uploaded to assets) and
* add it into the DMS storage, deleting the old/uploaded file.
@ -48,9 +43,12 @@ class DMSUploadField extends UploadField
// Otherwise create it
$doc = $dms->storeDocument($file);
$file->delete();
// Relate to the underlying page being edited.
// Not applicable when editing the document itself and replacing it.
$doc->addPage($record);
}
// Relate to the underlying document set being edited.
// Not applicable when editing the document itself and replacing it, or uploading from the ModelAdmin
if ($record instanceof DMSDocumentSet) {
$record->Documents()->add($doc, array('ManuallyAdded' => 1));
}
return $doc;
@ -61,25 +59,18 @@ class DMSUploadField extends UploadField
return true;
}
public function isDisabled()
{
return (parent::isDisabled() || !$this->isSaveable());
}
public function isSaveable()
{
return (!empty($this->getRecord()->ID));
}
/**
* Action to handle upload of a single file
*
*
* @param SS_HTTPRequest $request
* @return string json
*/
public function upload(SS_HTTPRequest $request)
{
if ($recordId = $request->postVar('ID')) {
$this->setRecord(DMSDocumentSet::get()->byId($recordId));
}
if ($this->isDisabled() || $this->isReadonly()) {
return $this->httpError(403);
}
@ -93,7 +84,7 @@ class DMSUploadField extends UploadField
$name = $this->getName();
$tmpfile = $request->postVar($name);
$record = $this->getRecord();
// Check if the file has been uploaded into the temporary storage.
if (!$tmpfile) {
$return = array('error' => _t('UploadField.FIELDNOTSET', 'File information not found'));
@ -110,13 +101,13 @@ class DMSUploadField extends UploadField
if (!$return['error'] && $this->relationAutoSetting && $record && $record->exists()) {
$tooManyFiles = false;
// Some relationships allow many files to be attached.
if ($this->getConfig('allowedMaxFileNumber') && ($record->has_many($name) || $record->many_many($name))) {
if ($this->getConfig('allowedMaxFileNumber') && ($record->hasMany($name) || $record->manyMany($name))) {
if (!$record->isInDB()) {
$record->write();
}
$tooManyFiles = $record->{$name}()->count() >= $this->getConfig('allowedMaxFileNumber');
// has_one only allows one file at any given time.
} elseif ($record->has_one($name)) {
} elseif ($record->hasOne($name)) {
$tooManyFiles = $record->{$name}() && $record->{$name}()->exists();
}
@ -141,13 +132,13 @@ class DMSUploadField extends UploadField
// Search for relations that can hold the uploaded files.
if ($relationClass = $this->getRelationAutosetClass()) {
// Create new object explicitly. Otherwise rely on Upload::load to choose the class.
$fileObject = Object::create($relationClass);
$fileObject = SS_Object::create($relationClass);
}
}
// Get the uploaded file into a new file object.
try {
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->folderName);
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$return['error'] = $e->getMessage();
@ -155,13 +146,13 @@ class DMSUploadField extends UploadField
if (!$return['error']) {
if ($this->upload->isError()) {
$return['error'] = implode(' '.PHP_EOL, $this->upload->getErrors());
$return['error'] = implode(' ' . PHP_EOL, $this->upload->getErrors());
} else {
$file = $this->upload->getFile();
// CUSTOM Attach the file to the related record.
$document = $this->attachFile($file);
// Collect all output data.
$return = array_merge($return, array(
'id' => $document->ID,
@ -198,10 +189,10 @@ class DMSUploadField extends UploadField
// Replace the download template with a new one only when access the upload field through a GridField.
// Needs to be enabled through setConfig('downloadTemplateName', 'ss-dmsuploadfield-downloadtemplate');
Requirements::javascript('dms/javascript/DMSUploadField_downloadtemplate.js');
Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_downloadtemplate.js');
// In the add dialog, add the addtemplate into the set of file that load.
Requirements::javascript('dms/javascript/DMSUploadField_addtemplate.js');
Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_addtemplate.js');
return $fields;
}
@ -214,7 +205,7 @@ class DMSUploadField extends UploadField
{
return DMSUploadField_ItemHandler::create($this, $itemID);
}
/**
* FieldList $fields for the EditForm
@ -223,24 +214,26 @@ class DMSUploadField extends UploadField
* @param File $file File context to generate fields for
* @return FieldList List of form fields
*/
public function getDMSFileEditFields($file)
{
public function getDMSFileEditFields($file)
{
// Empty actions, generate default
if(empty($this->fileEditFields)) {
if (empty($this->fileEditFields)) {
$fields = $file->getCMSFields();
// Only display main tab, to avoid overly complex interface
if($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
if ($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
$fields = $mainTab->Fields();
}
return $fields;
}
// Fields instance
if ($this->fileEditFields instanceof FieldList) return $this->fileEditFields;
if ($this->fileEditFields instanceof FieldList) {
return $this->fileEditFields;
}
// Method to call on the given file
if($file->hasMethod($this->fileEditFields)) {
if ($file->hasMethod($this->fileEditFields)) {
return $file->{$this->fileEditFields}();
}
@ -254,21 +247,23 @@ class DMSUploadField extends UploadField
* @param File $file File context to generate form actions for
* @return FieldList Field list containing FormAction
*/
public function getDMSFileEditActions($file)
{
public function getDMSFileEditActions($file)
{
// Empty actions, generate default
if(empty($this->fileEditActions)) {
if (empty($this->fileEditActions)) {
$actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
$saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
return $actions;
}
// Actions instance
if ($this->fileEditActions instanceof FieldList) return $this->fileEditActions;
if ($this->fileEditActions instanceof FieldList) {
return $this->fileEditActions;
}
// Method to call on the given file
if($file->hasMethod($this->fileEditActions)) {
if ($file->hasMethod($this->fileEditActions)) {
return $file->{$this->fileEditActions}();
}
@ -282,58 +277,45 @@ class DMSUploadField extends UploadField
* @param File $file File context to generate validator from
* @return Validator Validator object
*/
public function getDMSFileEditValidator($file)
{
public function getDMSFileEditValidator($file)
{
// Empty validator
if(empty($this->fileEditValidator)) return null;
if (empty($this->fileEditValidator)) {
return null;
}
// Validator instance
if($this->fileEditValidator instanceof Validator) return $this->fileEditValidator;
if ($this->fileEditValidator instanceof Validator) {
return $this->fileEditValidator;
}
// Method to call on the given file
if($file->hasMethod($this->fileEditValidator)) {
if ($file->hasMethod($this->fileEditValidator)) {
return $file->{$this->fileEditValidator}();
}
user_error("Invalid value for UploadField::fileEditValidator", E_USER_ERROR);
}
}
class DMSUploadField_ItemHandler extends UploadField_ItemHandler
{
private static $allowed_actions = array(
'delete',
'edit',
'EditForm',
);
public function getItem()
{
return DataObject::get_by_id('DMSDocument', $this->itemID);
}
/**
* @return Form
* Set the folder name to store DMS files in
*
* @param string $folderName
* @return $this
*/
public function EditForm() {
$file = $this->getItem();
// Get form components
$fields = $this->parent->getDMSFileEditFields($file);
$actions = $this->parent->getDMSFileEditActions($file);
$validator = $this->parent->getDMSFileEditValidator($file);
$form = new Form(
$this,
__FUNCTION__,
$fields,
$actions,
$validator
);
$form->loadDataFrom($file);
$form->addExtraClass('small');
return $form;
public function setFolderName($folderName)
{
$this->folderName = (string) $folderName;
return $this;
}
/**
* Get the folder name for storing the document
*
* @return string
*/
public function getFolderName()
{
return $this->folderName;
}
}

View File

@ -0,0 +1,43 @@
<?php
class DMSUploadField_ItemHandler extends UploadField_ItemHandler
{
private static $allowed_actions = array(
'delete',
'edit',
'EditForm',
);
/**
* Gets a DMS document by its ID
*
* @return DMSDocument
*/
public function getItem()
{
return DMSDocument::get()->byId($this->itemID);
}
/**
* @return Form
*/
public function EditForm()
{
$file = $this->getItem();
// Get form components
$fields = $this->parent->getDMSFileEditFields($file);
$actions = $this->parent->getDMSFileEditActions($file);
$validator = $this->parent->getDMSFileEditValidator($file);
$form = new Form(
$this,
__FUNCTION__,
$fields,
$actions,
$validator
);
$form->loadDataFrom($file);
return $form;
}
}

View File

@ -2,11 +2,8 @@
/**
* Extends the original toolbar with document picking capability - modified lines are commented.
*/
class DocumentHtmlEditorFieldToolbar extends Extension
{
public function updateLinkForm(Form $form)
{
$linkType = null;
@ -29,6 +26,8 @@ class DocumentHtmlEditorFieldToolbar extends Extension
$addExistingField->setUseFieldClass(false);
$fieldList->insertAfter($addExistingField, 'Description');
$fieldList->push(HiddenField::create('DMSShortcodeHandlerKey', false, DMS::inst()->getShortcodeHandlerKey()));
// Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
// Requirements::javascript(SAPPHIRE_DIR . "/javascript/tiny_mce_improvements.js");
//

View File

@ -0,0 +1,55 @@
<?php
class DMSDocumentTaxonomyExtension extends DataExtension
{
private static $many_many = array(
'Tags' => 'TaxonomyTerm'
);
/**
* Push an autocomplete dropdown for the available tags in documents
*
* @param FieldList $fields
*/
public function updateCMSFields(FieldList $fields)
{
$tags = $this->getAllTagsMap();
$tagField = ListboxField::create('Tags', _t('DMSDocumentTaxonomyExtension.TAGS', 'Tags'))
->setMultiple(true)
->setSource($tags);
if (empty($tags)) {
$tagField->setAttribute('data-placeholder', _t('DMSDocumentTaxonomyExtension.NOTAGS', 'No tags found'));
}
$fields->insertAfter('Description', $tagField);
}
/**
* Return an array of all the available tags that a document can use. Will return a list containing a taxonomy
* term's entire hierarchy, e.g. "Photo > Attribute > Density > High"
*
* @return array
*/
public function getAllTagsMap()
{
$tags = TaxonomyTerm::get()->filter(
'Type.Name:ExactMatch',
Config::inst()->get('DMSTaxonomyTypeExtension', 'default_record_name')
);
$map = array();
foreach ($tags as $tag) {
$nameParts = array($tag->Name);
$currentTag = $tag;
while ($currentTag->Parent() && $currentTag->Parent()->exists()) {
array_unshift($nameParts, $currentTag->Parent()->Name);
$currentTag = $currentTag->Parent();
}
$map[$tag->ID] = implode(' > ', $nameParts);
}
return $map;
}
}

View File

@ -5,137 +5,79 @@
*/
class DMSSiteTreeExtension extends DataExtension
{
private static $belongs_many_many = array(
'Documents' => 'DMSDocument'
private static $has_many = array(
'DocumentSets' => 'DMSDocumentSet'
);
private static $noDocumentsList = array();
private static $showDocumentsList = array();
/**
* Do not show the documents tab on the array of pages set here
* @static
* @param $mixed Array of page types to not show the Documents tab on
*/
public static function no_documents_tab($array = array())
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$noDocumentsList = $array;
} else {
self::$noDocumentsList = array($array);
}
}
/**
* Only show the documents tab on the list of pages set here. Any pages set in the no_documents_tab array will
* still not be shown. If this isn't called, or if it is called with an empty array, all pages will get Document tabs.
* @static
* @param $array Array of page types to show the Documents tab on
*/
public static function show_documents_tab($array = array())
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$showDocumentsList = $array;
} else {
self::$showDocumentsList = array($array);
}
}
public function updateCMSFields(FieldList $fields)
{
//prevent certain pages from having a Document tab in the CMS
if (in_array($this->owner->ClassName, self::$noDocumentsList)) {
return;
}
if (count(self::$showDocumentsList) > 0 && !in_array($this->owner->ClassName, self::$showDocumentsList)) {
// Ability to disable document sets for a Page
if (!$this->owner->config()->get('documents_enabled')) {
return;
}
//javascript to customize the grid field for the DMS document (overriding entwine in FRAMEWORK_DIR.'/javascript/GridField.js'
Requirements::javascript(DMS_DIR.'/javascript/DMSGridField.js');
Requirements::css(DMS_DIR.'/css/DMSMainCMS.css');
//javascript for the link editor pop-up in TinyMCE
Requirements::javascript(DMS_DIR."/javascript/DocumentHtmlEditorFieldToolbar.js");
// Document listing
$gridFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldToolbarHeader(),
new GridFieldFilterHeader(),
new GridFieldSortableHeader(),
new GridFieldOrderableRows('DocumentSort'),
new GridFieldDataColumns(),
new GridFieldEditButton(),
new DMSGridFieldDeleteAction(), //special delete dialog to handle custom behaviour of unlinking and deleting
new GridFieldDetailForm()
//GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
);
if (class_exists('GridFieldPaginatorWithShowAll')) {
$paginatorComponent = new GridFieldPaginatorWithShowAll(15);
} else {
$paginatorComponent = new GridFieldPaginator(15);
// Hides the DocumentSets tab if the user has no permisions
if (!Permission::checkMember(
Member::currentUser(),
array('ADMIN', 'CMS_ACCESS_DMSDocumentAdmin')
)
) {
return;
}
$gridFieldConfig->addComponent($paginatorComponent);
if (class_exists('GridFieldSortableRows')) {
$sortableComponent = new GridFieldSortableRows('DocumentSort');
//setUsePagenation method removed from newer version of SortableGridField.
if (method_exists($sortableComponent, 'setUsePagination')) {
$sortableComponent->setUsePagination(false)->setForceRedraw(true);
}
$gridFieldConfig->addComponent($sortableComponent);
}
// HACK: Create a singleton of DMSDocument to ensure extensions are applied before we try to get display fields.
singleton('DMSDocument');
$gridFieldConfig->getComponentByType('GridFieldDataColumns')->setDisplayFields(Config::inst()->get('DMSDocument', 'display_fields'))
->setFieldCasting(array('LastChanged'=>"Datetime->Ago"))
->setFieldFormatting(array('FilenameWithoutID'=>'<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>'));
//override delete functionality with this class
$gridFieldConfig->getComponentByType('GridFieldDetailForm')->setItemRequestClass('DMSGridFieldDetailForm_ItemRequest');
$gridField = GridField::create(
'Documents',
'DocumentSets',
false,
$this->owner->Documents()->Sort('DocumentSort'),
$gridFieldConfig
$this->owner->DocumentSets(), //->Sort('DocumentSort'),
$config = new GridFieldConfig_RelationEditor
);
$gridField->addExtraClass('documents');
$gridField->addExtraClass('documentsets');
$uploadBtn = new LiteralField(
'UploadButton',
sprintf(
'<a class="ss-ui-button ss-ui-action-constructive cms-panel-link" data-pjax-target="Content" data-icon="add" href="%s">%s</a>',
Controller::join_links(singleton('DMSDocumentAddController')->Link(), '?ID=' . $this->owner->ID),
"Add Documents"
)
// Only show document sets in the autocompleter that have not been assigned to a page already
$config->getComponentByType('GridFieldAddExistingAutocompleter')->setSearchList(
DMSDocumentSet::get()->filter(array('PageID' => 0))
);
$fields->addFieldsToTab(
'Root.Documents (' . $this->owner->Documents()->Count() . ')',
array(
$uploadBtn,
$gridField
)
$fields->addFieldToTab(
'Root.DocumentSets',
$gridField
);
$fields
->findOrMakeTab('Root.DocumentSets')
->setTitle(_t(
__CLASS__ . '.DocumentSetsTabTitle',
'Document Sets ({count})',
array('count' => $this->owner->DocumentSets()->count())
));
}
/**
* Enforce sorting for frontend
* Get a list of document sets for the owner page
*
* @return ArrayList
*/
public function PageDocuments()
public function getDocumentSets()
{
return $this->owner->getManyManyComponents('Documents')->sort('DocumentSort');
return $this->owner->DocumentSets();
}
/**
* Get a list of all documents from all document sets for the owner page
*
* @return ArrayList
*/
public function getAllDocuments()
{
$documents = ArrayList::create();
foreach ($this->getDocumentSets() as $documentSet) {
/** @var DocumentSet $documentSet */
$documents->merge($documentSet->getDocuments());
}
$documents->removeDuplicates();
return $documents;
}
public function onBeforeDelete()
@ -148,11 +90,11 @@ class DMSSiteTreeExtension extends DataExtension
// Only remove if record doesn't still exist on live stage.
if (!$existsOnOtherStage) {
$dmsDocuments = $this->owner->Documents();
$dmsDocuments = $this->owner->getAllDocuments();
foreach ($dmsDocuments as $document) {
//if the document is only associated with one page, i.e. only associated with this page
if ($document->Pages()->Count() <= 1) {
//delete the document before deleting this page
// If the document is only associated with one page, i.e. only associated with this page
if ($document->getRelatedPages()->count() <= 1) {
// Delete the document before deleting this page
$document->delete();
}
}
@ -161,8 +103,8 @@ class DMSSiteTreeExtension extends DataExtension
public function onBeforePublish()
{
$embargoedDocuments = $this->owner->Documents()->filter('EmbargoedUntilPublished', true);
if ($embargoedDocuments->Count() > 0) {
$embargoedDocuments = $this->owner->getAllDocuments()->filter('EmbargoedUntilPublished', true);
if ($embargoedDocuments->count() > 0) {
foreach ($embargoedDocuments as $doc) {
$doc->EmbargoedUntilPublished = false;
$doc->write();
@ -170,8 +112,14 @@ class DMSSiteTreeExtension extends DataExtension
}
}
/**
* Returns the title of the page with the total number of documents it has associated with it across
* all document sets
*
* @return string
*/
public function getTitleWithNumberOfDocuments()
{
return $this->owner->Title . ' (' . $this->owner->Documents()->Count() . ')';
return $this->owner->Title . ' (' . $this->owner->getAllDocuments()->count() . ')';
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Creates default taxonomy type records if they don't exist already
*/
class DMSTaxonomyTypeExtension extends DataExtension
{
/**
* Create default taxonomy type records. Add records via YAML configuration (see taxonomy.yml):
*
* <code>
* DMSTaxonomyTypeExtension:
* default_records:
* - Document
* - PrivateDocument
* </code>
*/
public function requireDefaultRecords()
{
$records = (array) Config::inst()->get(get_class($this), 'default_records');
foreach ($records as $name) {
$type = TaxonomyType::get()->filter('Name', $name)->first();
if (!$type) {
$type = TaxonomyType::create(array('Name' => $name));
$type->write();
}
}
}
}

118
code/forms/DMSJsonField.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* Combines form inputs into a key-value pair
*/
class DMSJsonField extends CompositeField
{
public function __construct($name, $children = null)
{
$this->setName($name);
if ($children instanceof FieldList || is_array($children)) {
foreach ($children as $child) {
$this->setChildName($child);
}
} else {
$children = is_array(func_get_args()) ? func_get_args() : array();
if (!empty($children)) {
array_shift($children);
}
foreach ($children as $child) {
$this->setChildName($child);
}
}
parent::__construct($children);
}
/**
* Sets the name of the child object
*
* @param FormField $child
*/
private function setChildName($child)
{
$child->setName("{$this->getName()}[{$child->getName()}]");
}
public function hasData()
{
return true;
}
/**
* Override parent's behaviour as it's no longer required
*
* @param array $list
* @param bool $saveableOnly
*/
public function collateDataFields(&$list, $saveableOnly = false)
{
}
/**
* Recursively removed empty key-value pairs from $haystack
*
* @param $haystack
*
* @return mixed
*/
public function arrayFilterEmptyRecursive($haystack)
{
foreach ($haystack as $key => $value) {
if (is_array($value)) {
$haystack[$key] = $this->arrayFilterEmptyRecursive($haystack[$key]);
}
if (empty($haystack[$key])) {
unset($haystack[$key]);
}
}
return $haystack;
}
/**
* Overrides parent behaviour to remove empty elements
*
* @return mixed|null|string
*/
public function dataValue()
{
$result = null;
if (is_array($this->value)) {
$this->value = $this->arrayFilterEmptyRecursive($this->value);
$result = (!empty($this->value)) ? Convert::array2json($this->value) : $result;
} else {
$result = parent::dataValue();
}
return $result;
}
/**
* Sets the value
* @param mixed $value
*
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
if (is_string($value) && !empty($value)) {
$value = Convert::json2array($value);
} elseif (!is_array($value)) {
$value = array($value);
}
$pattern = "/^{$this->getName()}\[(.*)\]$/";
foreach ($this->children as $c) {
$title = $c->getName();
preg_match($pattern, $title, $matches);
if (!empty($matches[1]) && isset($value[$matches[1]])) {
$c->setValue($value[$matches[1]]);
}
}
return $this;
}
}

View File

@ -1,117 +1,22 @@
<?php
/**
* Interface for a DMSDocument used in the Document Management System. A DMSDocument is create by storing a File object in an
* instance of the DMSInterface. All write operations on the DMSDocument create a new relation, so we never need to
* explicitly call the write() method on the DMSDocument DataObject
* Interface for a DMSDocument used in the Document Management System. A DMSDocument is create by storing a File
* object in an instance of the DMSInterface. All write operations on the DMSDocument create a new relation, so we
* never need to explicitly call the write() method on the DMSDocument DataObject
*/
interface DMSDocumentInterface
{
/**
* Deletes the DMSDocument, its underlying file, as well as any tags related to this DMSDocument.
*
* @todo Can't be applied to classes which already implement the DataObjectInterface (naming conflict)
*
* @abstract
* @return null
*/
// function delete();
/**
* Associates this DMSDocument with a Page. This method does nothing if the association already exists.
* This could be a simple wrapper around $myDoc->Pages()->add($myPage) to add a many_many relation
* @abstract
* @param $pageObject Page object to associate this DMSDocument with
* @return null
*/
public function addPage($pageObject);
/**
* Associates this DMSDocument with a set of Pages. This method loops through a set of page ids, and then associates this
* DMSDocument with the individual Page with the each page id in the set
* @abstract
* @param $pageIDs array of page ids used for the page objects associate this DMSDocument with
* @return null
*/
public function addPages($pageIDs);
/**
* Removes the association between this DMSDocument and a Page. This method does nothing if the association does not exist.
* @abstract
* @param $pageObject Page object to remove the association to
* @return mixed
*/
public function removePage($pageObject);
/**
* Returns a list of the Page objects associated with this DMSDocument
* @abstract
* @return DataList
*/
public function getPages();
/**
* Removes all associated Pages from the DMSDocument
* @abstract
* @return null
*/
public function removeAllPages();
/**
* Adds a metadata tag to the DMSDocument. The tag has a category and a value.
* Each category can have multiple values by default. So: addTag("fruit","banana") addTag("fruit", "apple") will add two items.
* However, if the third parameter $multiValue is set to 'false', then all updates to a category only ever update a single value. So:
* addTag("fruit","banana") addTag("fruit", "apple") would result in a single metadata tag: fruit->apple.
* Can could be implemented as a key/value store table (although it is more like category/value, because the
* same category can occur multiple times)
* @abstract
* @param $category String of a metadata category to add (required)
* @param $value String of a metadata value to add (required)
* @param bool $multiValue Boolean that determines if the category is multi-value or single-value (optional)
* @return null
*/
public function addTag($category, $value, $multiValue = true);
/**
* Fetches all tags associated with this DMSDocument within a given category. If a value is specified this method
* tries to fetch that specific tag.
* @abstract
* @param $category String of the metadata category to get
* @param null $value String of the value of the tag to get
* @return array of Strings of all the tags or null if there is no match found
*/
public function getTagsList($category, $value = null);
/**
* Removes a tag from the DMSDocument. If you only set a category, then all values in that category are deleted.
* If you specify both a category and a value, then only that single category/value pair is deleted.
* Nothing happens if the category or the value do not exist.
* @abstract
* @param $category Category to remove (required)
* @param null $value Value to remove (optional)
* @return null
*/
public function removeTag($category, $value = null);
/**
* Deletes all tags associated with this DMSDocument.
* @abstract
* @return null
*/
public function removeAllTags();
/**
* Returns a link to download this DMSDocument from the DMS store
* @abstract
* @return String
*/
public function getLink();
/**
* Return the extension of the file associated with the document
*/
public function getExtension();
/**
* Returns the size of the file type in an appropriate format.
*
@ -139,14 +44,12 @@ interface DMSDocumentInterface
* Hides the DMSDocument, so it does not show up when getByPage($myPage) is called
* (without specifying the $showEmbargoed = true parameter). This is similar to expire, except that this method
* should be used to hide DMSDocuments that have not yet gone live.
* @abstract
* @return null
*/
public function embargoIndefinitely();
/**
* Returns if this is DMSDocument is embargoed or expired.
* @abstract
* @return bool True or False depending on whether this DMSDocument is embargoed or expired
*/
public function isHidden();
@ -154,7 +57,6 @@ interface DMSDocumentInterface
/**
* Returns if this is DMSDocument is embargoed.
* @abstract
* @return bool True or False depending on whether this DMSDocument is embargoed
*/
public function isEmbargoed();
@ -162,7 +64,6 @@ interface DMSDocumentInterface
/**
* Hides the DMSDocument, so it does not show up when getByPage($myPage) is called. Automatically un-hides the
* DMSDocument at a specific date.
* @abstract
* @param $datetime String date time value when this DMSDocument should expire
* @return null
*/
@ -176,21 +77,18 @@ interface DMSDocumentInterface
/**
* Clears any previously set embargos, so the DMSDocument always shows up in all queries.
* @abstract
* @return null
*/
public function clearEmbargo();
/**
* Returns if this is DMSDocument is expired.
* @abstract
* @return bool True or False depending on whether this DMSDocument is expired
*/
public function isExpired();
/**
* Hides the DMSDocument at a specific date, so it does not show up when getByPage($myPage) is called.
* @abstract
* @param $datetime String date time value when this DMSDocument should expire
* @return null
*/
@ -198,7 +96,6 @@ interface DMSDocumentInterface
/**
* Clears any previously set expiry.
* @abstract
* @return null
*/
public function clearExpiry();
@ -208,7 +105,6 @@ interface DMSDocumentInterface
/**
* Returns a DataList of all previous Versions of this DMSDocument (check the LastEdited date of each
* object to find the correct one)
* @abstract
* @return DataList List of DMSDocument objects
*/
public function getVersions();

View File

@ -10,7 +10,6 @@
*/
interface DMSInterface
{
/**
* Factory method that returns an instance of the DMS. This could be any class that implements the DMSInterface.
* @static
@ -28,18 +27,6 @@ interface DMSInterface
*/
public function storeDocument($file);
/**
*
* Returns a number of Document objects based on the a search by tags. You can search by category alone,
* by tag value alone, or by both. I.e: getByTag("fruits",null); getByTag(null,"banana"); getByTag("fruits","banana")
* @abstract
* @param null $category The metadata category to search for
* @param null $value The metadata value to search for
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DMSDocumentInterface
*/
public function getByTag($category = null, $value = null, $showEmbargoed = false);
/**
* Returns a number of Document objects that match a full-text search of the Documents and their contents
* (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module)
@ -52,11 +39,19 @@ interface DMSInterface
/**
* Returns a list of Document objects associated with a Page
* @abstract
* @param $page SiteTree to fetch the associated Documents from
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DataList Document list associated with the Page
* Returns a list of Document objects associated with a Page via intermediary document sets
*
* @param SiteTree $page SiteTree to fetch the associated Documents from
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return ArrayList Document list associated with the Page
*/
public function getByPage($page, $showEmbargoed = false);
public function getByPage(SiteTree $page, $showEmbargoed = false);
/**
* Returns a list of Document Set objects associated with a Page
*
* @param SiteTree $page SiteTree to fetch the associated Document Sets from
* @return ArrayList Document list associated with the Page
*/
public function getDocumentSetsByPage(SiteTree $page);
}

View File

@ -2,55 +2,79 @@
/**
* @package dms
*
* @property Varchar Filename
* @property Varchar Folder
* @property Varchar Title
* @property Text Description
* @property int ViewCount
* @property Boolean EmbargoedIndefinitely
* @property Boolean EmbargoedUntilPublished
* @property DateTime EmbargoedUntilDate
* @property DateTime ExpireAtDate
* @property Enum DownloadBehavior
* @property Enum CanViewType Enum('Anyone, LoggedInUsers, OnlyTheseUsers', 'Anyone')
* @property Enum CanEditType Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers')
*
* @method ManyManyList RelatedDocuments
* @method ManyManyList ViewerGroups
* @method ManyManyList EditorGroups
*
* @method Member CreatedBy
* @property Int CreatedByID
* @method Member LastEditedBy
* @property Int LastEditedByID
*
*/
class DMSDocument extends DataObject implements DMSDocumentInterface
{
private static $db = array(
"Filename" => "Varchar(255)", // eg. 3469~2011-energysaving-report.pdf
"Folder" => "Varchar(255)", // eg. 0
"Title" => 'Varchar(1024)', // eg. "Energy Saving Report for Year 2011, New Zealand LandCorp"
"Description" => 'Text',
"ViewCount" => 'Int',
"LastChanged" => 'SS_DateTime', //when this document's file was created or last replaced (small changes like updating title don't count)
"EmbargoedIndefinitely" => 'Boolean(false)',
"EmbargoedUntilPublished" => 'Boolean(false)',
"EmbargoedUntilDate" => 'SS_DateTime',
"ExpireAtDate" => 'SS_DateTime',
"DownloadBehavior" => 'Enum(array("open","download"), "download")',
"CanViewType" => "Enum('Anyone, LoggedInUsers, OnlyTheseUsers', 'Anyone')",
"CanEditType" => "Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers')",
);
private static $belongs_many_many = array(
'Sets' => 'DMSDocumentSet'
);
private static $has_one = array(
'CoverImage' => 'Image',
'CreatedBy' => 'Member',
'LastEditedBy' => 'Member',
);
private static $many_many = array(
'Pages' => 'SiteTree',
'Tags' => 'DMSTag'
);
private static $many_many_extraFields = array(
'Pages' => array(
'DocumentSort' => 'Int'
)
'RelatedDocuments' => 'DMSDocument',
'ViewerGroups' => 'Group',
'EditorGroups' => 'Group',
);
private static $display_fields = array(
'ID' => 'ID',
'Title' => 'Title',
'FilenameWithoutID' => 'Filename',
'LastChanged' => 'LastChanged'
'LastEdited' => 'Last Edited'
);
private static $singular_name = 'Document';
private static $plural_name = 'Documents';
private static $searchable_fields = array(
'ID' => array(
'filter' => 'ExactMatchFilter',
'field' => 'NumericField'
),
'Title',
'Filename',
'LastChanged'
private static $summary_fields = array(
'Filename' => 'Filename',
'Title' => 'Title',
'getRelatedPages.count' => 'Page Use',
'ViewCount' => 'ViewCount',
);
/**
@ -60,10 +84,20 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
private static $default_download_behaviour = 'download';
/**
* @param Member $member
* A key value map of the "actions" tabs that will be added to the CMS fields
*
* @return boolean
* @var array
*/
protected $actionTasks = array(
'embargo' => 'Embargo',
'expiry' => 'Expiry',
'replace' => 'Replace',
'find-usage' => 'Usage',
'find-references' => 'References',
'find-relateddocuments' => 'Related Documents',
'permissions' => 'Permissions'
);
public function canView($member = null)
{
if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -79,18 +113,33 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
if ($member && $member->ID) {
if (!$this->CanViewType || $this->CanViewType == 'Anyone') {
return true;
}
if ($member && Permission::checkMember($member, array(
'ADMIN',
'SITETREE_EDIT_ALL',
'SITETREE_VIEW_ALL',
))
) {
return true;
}
return false;
if ($this->isHidden()) {
return false;
}
if ($this->CanViewType == 'LoggedInUsers') {
return $member && $member->exists();
}
if ($this->CanViewType == 'OnlyTheseUsers' && $this->ViewerGroups()->count()) {
return ($member && $member->inGroups($this->ViewerGroups()) || $this->canEdit($member));
}
return $this->canEdit($member);
}
/**
* @param Member $member
*
* @return boolean
*/
public function canEdit($member = null)
{
if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -105,7 +154,20 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
return $this->canView();
// Do early admin check
if ($member && Permission::checkMember($member, array('ADMIN','SITETREE_EDIT_ALL'))) {
return true;
}
if ($this->CanEditType === 'LoggedInUsers') {
return $member && $member->exists();
}
if ($this->CanEditType === 'OnlyTheseUsers' && $this->EditorGroups()->count()) {
return $member && $member->inGroups($this->EditorGroups());
}
return false;
}
/**
@ -127,7 +189,14 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
return $this->canView();
// Do early admin check
if ($member &&
Permission::checkMember($member, array('CMS_ACCESS_DMSDocumentAdmin'))
) {
return true;
}
return $this->canEdit($member);
}
/**
@ -149,101 +218,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
return $this->canView();
}
/**
* Associates this document with a Page. This method does nothing if the
* association already exists.
*
* This could be a simple wrapper around $myDoc->Pages()->add($myPage) to
* add a many_many relation.
*
* @param SiteTree $pageObject Page object to associate this Document with
*
* @return DMSDocument
*/
public function addPage($pageObject)
{
$this->Pages()->add($pageObject);
DB::query("UPDATE \"DMSDocument_Pages\" SET \"DocumentSort\"=\"DocumentSort\"+1 WHERE \"SiteTreeID\" = $pageObject->ID");
return $this;
}
/**
* Associates this DMSDocument with a set of Pages. This method loops
* through a set of page ids, and then associates this DMSDocument with the
* individual Page with the each page id in the set.
*
* @param array $pageIDs
*
* @return DMSDocument
*/
public function addPages($pageIDs)
{
foreach ($pageIDs as $id) {
$pageObject = DataObject::get_by_id("SiteTree", $id);
if ($pageObject && $pageObject->exists()) {
$this->addPage($pageObject);
}
}
return $this;
}
/**
* Removes the association between this Document and a Page. This method
* does nothing if the association does not exist.
*
* @param SiteTree $pageObject Page object to remove the association to
*
* @return DMSDocument
*/
public function removePage($pageObject)
{
$this->Pages()->remove($pageObject);
return $this;
}
/**
* @see getPages()
*
* @return DataList
*/
public function Pages()
{
$pages = $this->getManyManyComponents('Pages');
$this->extend('updatePages', $pages);
return $pages;
}
/**
* Returns a list of the Page objects associated with this Document.
*
* @return DataList
*/
public function getPages()
{
return $this->Pages();
}
/**
* Removes all associated Pages from the DMSDocument
*
* @return DMSDocument
*/
public function removeAllPages()
{
$this->Pages()->removeAll();
return $this;
return $this->canEdit($member);
}
/**
@ -260,173 +235,8 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$this->ViewCount = $count;
DB::query("UPDATE \"DMSDocument\" SET \"ViewCount\"='$count' WHERE \"ID\"={$this->ID}");
}
return $this;
}
/**
* Adds a metadata tag to the Document. The tag has a category and a value.
*
* Each category can have multiple values by default. So:
* addTag("fruit","banana") addTag("fruit", "apple") will add two items.
*
* However, if the third parameter $multiValue is set to 'false', then all
* updates to a category only ever update a single value. So:
* addTag("fruit","banana") addTag("fruit", "apple") would result in a
* single metadata tag: fruit->apple.
*
* Can could be implemented as a key/value store table (although it is more
* like category/value, because the same category can occur multiple times)
*
* @param string $category of a metadata category to add (required)
* @param string $value of a metadata value to add (required)
* @param bool $multiValue Boolean that determines if the category is
* multi-value or single-value (optional)
*
* @return DMSDocument
*/
public function addTag($category, $value, $multiValue = true)
{
if ($multiValue) {
//check for a duplicate tag, don't add the duplicate
$currentTag = $this->Tags()->filter(array('Category' => $category, 'Value' => $value));
if ($currentTag->Count() == 0) {
//multi value tag
$tag = new DMSTag();
$tag->Category = $category;
$tag->Value = $value;
$tag->MultiValue = true;
$tag->write();
$tag->Documents()->add($this);
} else {
//add the relation between the tag and document
foreach ($currentTag as $tagObj) {
$tagObj->Documents()->add($this);
}
}
} else {
//single value tag
$currentTag = $this->Tags()->filter(array('Category' => $category));
$tag = null;
if ($currentTag->Count() == 0) {
//create the single-value tag
$tag = new DMSTag();
$tag->Category = $category;
$tag->Value = $value;
$tag->MultiValue = false;
$tag->write();
} else {
//update the single value tag
$tag = $currentTag->first();
$tag->Value = $value;
$tag->MultiValue = false;
$tag->write();
}
// regardless of whether we created a new tag or are just updating an
// existing one, add the relation
$tag->Documents()->add($this);
}
return $this;
}
/**
* @param string $category
* @param string $value
*
* @return DataList
*/
protected function getTagsObjects($category, $value = null)
{
$valueFilter = array("Category" => $category);
if (!empty($value)) {
$valueFilter['Value'] = $value;
}
$tags = $this->Tags()->filter($valueFilter);
return $tags;
}
/**
* Fetches all tags associated with this DMSDocument within a given
* category. If a value is specified this method tries to fetch that
* specific tag.
*
* @param string $category metadata category to get
* @param string $value value of the tag to get
*
* @return array Strings of all the tags or null if there is no match found
*/
public function getTagsList($category, $value = null)
{
$tags = $this->getTagsObjects($category, $value);
$returnArray = null;
if ($tags->Count() > 0) {
$returnArray = array();
foreach ($tags as $t) {
$returnArray[] = $t->Value;
}
}
return $returnArray;
}
/**
* Removes a tag from the Document. If you only set a category, then all
* values in that category are deleted.
*
* If you specify both a category and a value, then only that single
* category/value pair is deleted.
*
* Nothing happens if the category or the value do not exist.
*
* @param string $category Category to remove
* @param string $value Value to remove
*
* @return DMSDocument
*/
public function removeTag($category, $value = null)
{
$tags = $this->getTagsObjects($category, $value);
if ($tags->Count() > 0) {
foreach ($tags as $t) {
$documentList = $t->Documents();
//remove the relation between the tag and the document
$documentList->remove($this);
//delete the entire tag if it has no relations left
if ($documentList->Count() == 0) {
$t->delete();
}
}
}
return $this;
}
/**
* Deletes all tags associated with this Document.
*
* @return DMSDocument
*/
public function removeAllTags()
{
$allTags = $this->Tags();
foreach ($allTags as $tag) {
$documentlist = $tag->Documents();
$documentlist->remove($this);
if ($tag->Documents()->Count() == 0) {
$tag->delete();
}
$this->extend('trackView');
}
return $this;
@ -434,12 +244,31 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
/**
* Returns a link to download this document from the DMS store.
* Alternatively a basic javascript alert will be shown should the user not have view permissions. An extension
* point for this was also added.
*
* To extend use the following from within an Extension subclass:
*
* <code>
* public function updateGetLink($result)
* {
* // Do something here
* }
* </code>
*
* @return string
*/
public function getLink()
{
return Controller::join_links(Director::baseURL(), 'dmsdocument/'.$this->ID);
$urlSegment = sprintf('%d-%s', $this->ID, URLSegmentFilter::create()->filter($this->getTitle()));
$result = Controller::join_links(Director::baseURL(), 'dmsdocument/' . $urlSegment);
if (!$this->canView()) {
$result = sprintf("javascript:alert('%s')", $this->getPermissionDeniedReason());
}
$this->extend('updateGetLink', $result);
return $result;
}
/**
@ -670,7 +499,8 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
public function getFullPath()
{
if ($this->Filename) {
return DMS::get_dms_path() . DIRECTORY_SEPARATOR . $this->Folder . DIRECTORY_SEPARATOR . $this->Filename;
return DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR
. $this->Folder . DIRECTORY_SEPARATOR . $this->Filename;
}
return null;
@ -681,13 +511,12 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
*
* @return string
*/
public function getFileName()
public function getFilename()
{
if ($this->getField('Filename')) {
return $this->getField('Filename');
} else {
return ASSETS_DIR . '/';
}
return ASSETS_DIR . '/';
}
/**
@ -700,6 +529,8 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
/**
* Returns the filename of a document without the prefix, e.g. 0~filename.jpg -> filename.jpg
*
* @return string
*/
public function getFilenameWithoutID()
@ -715,21 +546,17 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
*/
public function getStorageFolder()
{
return DMS::get_dms_path() . DIRECTORY_SEPARATOR . DMS::get_storage_folder($this->ID);
return DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . DMS::inst()->getStorageFolder($this->ID);
}
/**
* Deletes the DMSDocument, its underlying file, as well as any tags related
* to this DMSDocument. Also calls the parent DataObject's delete method in
* Deletes the DMSDocument and its underlying file. Also calls the parent DataObject's delete method in
* order to complete an cascade.
*
* @return void
*/
public function delete()
{
// remove tags
$this->removeAllTags();
// delete the file (and previous versions of files)
$filesToDelete = array();
$storageFolder = $this->getStorageFolder();
@ -756,8 +583,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
$this->removeAllPages();
// get rid of any versions have saved for this DMSDocument, too
if (DMSDocument_versions::$enable_versions) {
$versions = $this->getVersions();
@ -769,15 +594,13 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
parent::delete();
return parent::delete();
}
/**
* Relate an existing file on the filesystem to the document.
*
* Copies the file to the new destination, as defined in {@link get_DMS_path()}.
* Copies the file to the new destination, as defined in {@link DMS::getStoragePath()}.
*
* @param string $filePath Path to file, relative to webroot.
*
@ -792,10 +615,10 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
// calculate all the path to copy the file to
$fromFilename = basename($filePath);
$toFilename = $this->ID. '~' . $fromFilename; //add the docID to the start of the Filename
$toFolder = DMS::get_storage_folder($this->ID);
$toPath = DMS::get_dms_path() . DIRECTORY_SEPARATOR . $toFolder . DIRECTORY_SEPARATOR . $toFilename;
$toFolder = DMS::inst()->getStorageFolder($this->ID);
$toPath = DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $toFolder . DIRECTORY_SEPARATOR . $toFilename;
DMS::create_storage_folder(DMS::get_dms_path() . DIRECTORY_SEPARATOR . $toFolder);
DMS::inst()->createStorageFolder(DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $toFolder);
//copy the file into place
$fromPath = BASE_PATH . DIRECTORY_SEPARATOR . $filePath;
@ -823,7 +646,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$this->Title = basename($filePath, '.'.$extension);
}
$this->LastChanged = SS_Datetime::now()->Rfc2822();
$this->write();
return $this;
@ -840,7 +662,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
*/
public function replaceDocument($file)
{
$filePath = DMS::transform_file_to_file_path($file);
$filePath = DMS::inst()->transformFileToFilePath($file);
$doc = $this->storeDocument($filePath); // replace the document
return $doc;
@ -902,8 +724,8 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
public function getCMSFields()
{
//include JS to handling showing and hiding of bottom "action" tabs
Requirements::javascript(DMS_DIR.'/javascript/DMSDocumentCMSFields.js');
Requirements::css(DMS_DIR.'/css/DMSDocumentCMSFields.css');
Requirements::javascript(DMS_DIR . '/javascript/DMSDocumentCMSFields.js');
Requirements::css(DMS_DIR . '/dist/css/cmsbundle.css');
$fields = new FieldList(); //don't use the automatic scaffolding, it is slow and unnecessary here
@ -916,8 +738,14 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$fieldsTop = $this->getFieldsForFile($relationList->count());
$fields->add($fieldsTop);
$fields->add(new TextField('Title', 'Title'));
$fields->add(new TextareaField('Description', 'Description'));
$fields->add(TextField::create('Title', _t('DMSDocument.TITLE', 'Title')));
$fields->add(TextareaField::create('Description', _t('DMSDocument.DESCRIPTION', 'Description')));
$coverImageField = UploadField::create('CoverImage', _t('DMSDocument.COVERIMAGE', 'Cover Image'));
$coverImageField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
$coverImageField->setConfig('allowedMaxFileNumber', 1);
$fields->add($coverImageField);
$downloadBehaviorSource = array(
'open' => _t('DMSDocument.OPENINBROWSER', 'Open in browser'),
@ -926,8 +754,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$defaultDownloadBehaviour = Config::inst()->get('DMSDocument', 'default_download_behaviour');
if (!isset($downloadBehaviorSource[$defaultDownloadBehaviour])) {
user_error('Default download behaviour "' . $defaultDownloadBehaviour . '" not supported.', E_USER_WARNING);
}
else {
} else {
$downloadBehaviorSource[$defaultDownloadBehaviour] .= ' (' . _t('DMSDocument.DEFAULT', 'default') . ')';
}
@ -938,7 +765,10 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$downloadBehaviorSource,
$defaultDownloadBehaviour
)
->setDescription('How the visitor will view this file. <strong>Open in browser</strong> allows files to be opened in a new tab.')
->setDescription(
'How the visitor will view this file. <strong>Open in browser</strong> '
. 'allows files to be opened in a new tab.'
)
);
//create upload field to replace document
@ -958,12 +788,12 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$gridFieldConfig->getComponentByType('GridFieldDataColumns')
->setDisplayFields(array(
'Title'=>'Title',
'ClassName'=>'Page Type',
'ID'=>'Page ID'
'Title' => 'Title',
'ClassName' => 'Page Type',
'ID' => 'Page ID'
))
->setFieldFormatting(array(
'Title'=>sprintf(
'Title' => sprintf(
'<a class=\"cms-panel-link\" href=\"%s/$ID\">$Title</a>',
singleton('CMSPageEditController')->Link('show')
)
@ -972,7 +802,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$pagesGrid = GridField::create(
'Pages',
_t('DMSDocument.RelatedPages', 'Related Pages'),
$this->Pages(),
$this->getRelatedPages(),
$gridFieldConfig
);
@ -990,9 +820,14 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
new GridFieldDataColumns(),
new GridFieldPaginator(30)
);
$versionsGridFieldConfig->getComponentByType('GridFieldDataColumns')->setDisplayFields(Config::inst()->get('DMSDocument_versions', 'display_fields'))
->setFieldCasting(array('LastChanged'=>"Datetime->Ago"))
->setFieldFormatting(array('FilenameWithoutID'=>'<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>'));
$versionsGridFieldConfig->getComponentByType('GridFieldDataColumns')
->setDisplayFields(Config::inst()->get('DMSDocument_versions', 'display_fields'))
->setFieldFormatting(
array(
'FilenameWithoutID' => '<a target=\"_blank\" class=\"file-url\" href=\"$Link\">$FilenameWithoutID</a>'
)
);
$versionsGrid = GridField::create(
'Versions',
@ -1000,21 +835,9 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$this->getVersions(),
$versionsGridFieldConfig
);
$extraTasks .= '<li class="ss-ui-button" data-panel="find-versions">Versions</li>';
//$extraFields = $versionsGrid->addExtraClass('find-versions');
$this->addActionPanelTask('find-versions', 'Versions');
}
$fields->add(new LiteralField('BottomTaskSelection',
'<div id="Actions" class="field actions"><label class="left">Actions</label><ul>'.
'<li class="ss-ui-button" data-panel="embargo">Embargo</li>'.
'<li class="ss-ui-button" data-panel="expiry">Expiry</li>'.
'<li class="ss-ui-button" data-panel="replace">Replace</li>'.
'<li class="ss-ui-button" data-panel="find-usage">Usage</li>'.
'<li class="ss-ui-button" data-panel="find-references">References</li>'.
$extraTasks.
'</ul></div>'
));
$embargoValue = 'None';
if ($this->EmbargoedIndefinitely) {
$embargoValue = 'Indefinitely';
@ -1023,78 +846,127 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
} elseif (!empty($this->EmbargoedUntilDate)) {
$embargoValue = 'Date';
}
$embargo = new OptionsetField('Embargo', 'Embargo', array('None'=>'None', 'Published'=>'Hide document until page is published', 'Indefinitely'=>'Hide document indefinitely', 'Date'=>'Hide until set date'), $embargoValue);
$embargo = new OptionsetField(
'Embargo',
_t('DMSDocument.EMBARGO', 'Embargo'),
array(
'None' => _t('DMSDocument.EMBARGO_NONE', 'None'),
'Published' => _t('DMSDocument.EMBARGO_PUBLISHED', 'Hide document until page is published'),
'Indefinitely' => _t('DMSDocument.EMBARGO_INDEFINITELY', 'Hide document indefinitely'),
'Date' => _t('DMSDocument.EMBARGO_DATE', 'Hide until set date')
),
$embargoValue
);
$embargoDatetime = DatetimeField::create('EmbargoedUntilDate', '');
$embargoDatetime->getDateField()->setConfig('showcalendar', true)->setConfig('dateformat', 'dd-MM-yyyy')->setConfig('datavalueformat', 'dd-MM-yyyy');
$embargoDatetime->getDateField()
->setConfig('showcalendar', true)
->setConfig('dateformat', 'dd-MM-yyyy')
->setConfig('datavalueformat', 'dd-MM-yyyy');
$expiryValue = 'None';
if (!empty($this->ExpireAtDate)) {
$expiryValue = 'Date';
}
$expiry = new OptionsetField('Expiry', 'Expiry', array('None'=>'None', 'Date'=>'Set document to expire on'), $expiryValue);
$expiry = new OptionsetField(
'Expiry',
'Expiry',
array(
'None' => 'None',
'Date' => 'Set document to expire on'
),
$expiryValue
);
$expiryDatetime = DatetimeField::create('ExpireAtDate', '');
$expiryDatetime->getDateField()->setConfig('showcalendar', true)->setConfig('dateformat', 'dd-MM-yyyy')->setConfig('datavalueformat', 'dd-MM-yyyy');
$expiryDatetime->getDateField()
->setConfig('showcalendar', true)
->setConfig('dateformat', 'dd-MM-yyyy')
->setConfig('datavalueformat', 'dd-MM-yyyy');
// This adds all the actions details into a group.
// Embargo, History, etc to go in here
// These are toggled on and off via the Actions Buttons above
// exit('hit');
$actionsPanel = FieldGroup::create(
FieldGroup::create(
$embargo,
$embargoDatetime
)->addExtraClass('embargo'),
FieldGroup::create(
$expiry,
$expiryDatetime
)->addExtraClass('expiry'),
FieldGroup::create(
$uploadField
)->addExtraClass('replace'),
FieldGroup::create(
$pagesGrid
)->addExtraClass('find-usage'),
FieldGroup::create(
$referencesGrid
)->addExtraClass('find-references'),
FieldGroup::create(
$versionsGrid
)->addExtraClass('find-versions')
FieldGroup::create($embargo, $embargoDatetime)->addExtraClass('embargo'),
FieldGroup::create($expiry, $expiryDatetime)->addExtraClass('expiry'),
FieldGroup::create($uploadField)->addExtraClass('replace'),
FieldGroup::create($pagesGrid)->addExtraClass('find-usage'),
FieldGroup::create($referencesGrid)->addExtraClass('find-references'),
FieldGroup::create($this->getPermissionsActionPanel())->addExtraClass('permissions')
);
$actionsPanel->setName("ActionsPanel");
$actionsPanel->addExtraClass("DMSDocumentActionsPanel");
if ($this->canEdit()) {
$actionsPanel->push(FieldGroup::create($versionsGrid)->addExtraClass('find-versions'));
$actionsPanel->push(
FieldGroup::create($this->getRelatedDocumentsGridField())->addExtraClass('find-relateddocuments')
);
} else {
$this->removeActionPanelTask('find-relateddocuments')->removeActionPanelTask('find-versions');
}
$fields->add(LiteralField::create('BottomTaskSelection', $this->getActionTaskHtml()));
$actionsPanel->setName('ActionsPanel');
$actionsPanel->addExtraClass('dmsdocument-actionspanel');
$fields->push($actionsPanel);
// $fields->add(FieldGroup::create(
// FieldGroup::create(
// $embargo,
// $embargoDatetime
// )->addExtraClass('embargo'),
// FieldGroup::create(
// $expiry,
// $expiryDatetime
// )->addExtraClass('expiry'),
// $uploadField->addExtraClass('replace'),
// $pagesGrid->addExtraClass('find-usage'),
// $referencesGrid->addExtraClass('find-references'),
// $extraFields
// )->setName("ActionsPanel")->addExtraClass('dmsupload ss-uploadfield'));
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* Adds permissions selection fields to a composite field and returns so it can be used in the "actions panel"
*
* @return CompositeField
*/
public function getPermissionsActionPanel()
{
$fields = FieldList::create();
$showFields = array(
'CanViewType' => '',
'ViewerGroups' => 'hide',
'CanEditType' => '',
'EditorGroups' => 'hide',
);
/** @var SiteTree $siteTree */
$siteTree = singleton('SiteTree');
$settingsFields = $siteTree->getSettingsFields();
foreach ($showFields as $name => $extraCss) {
$compositeName = "Root.Settings.$name";
/** @var FormField $field */
if ($field = $settingsFields->fieldByName($compositeName)) {
$field->addExtraClass($extraCss);
$title = str_replace('page', 'document', $field->Title());
$field->setTitle($title);
// Remove Inherited source option from DropdownField
if ($field instanceof DropdownField) {
$options = $field->getSource();
unset($options['Inherit']);
$field->setSource($options);
}
$fields->push($field);
}
}
$this->extend('updatePermissionsFields', $fields);
return CompositeField::create($fields);
}
/**
* Return a title to use on the frontend, preferably the "title", otherwise the filename without it's numeric ID
*
* @return string
*/
public function getTitle()
{
if ($this->getField('Title')) {
return $this->getField('Title');
}
return $this->FilenameWithoutID;
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
@ -1103,7 +975,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
//set the embargo options from the OptionSetField created in the getCMSFields method
//do not write after clearing the embargo (write happens automatically)
$savedDate = $this->EmbargoedUntilDate;
$this->clearEmbargo(false); //clear all previous settings and re-apply them on save
$this->clearEmbargo(false); // Clear all previous settings and re-apply them on save
if ($this->Embargo == 'Published') {
$this->embargoUntilPublished(false);
@ -1121,7 +993,30 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$this->expireAtDate($this->ExpireAtDate, false);
} else {
$this->clearExpiry(false);
} //clear all previous settings
} // Clear all previous settings
}
// Set user fields
if ($currentUserID = Member::currentUserID()) {
if (!$this->CreatedByID) {
$this->CreatedByID = $currentUserID;
}
$this->LastEditedByID = $currentUserID;
}
// make sure default DownloadBehavior is respected when initially writing document
// in case the default in the enum is different than what's set in an outside config
$defaultDownloadBehaviour = Config::inst()->get('DMSDocument', 'default_download_behaviour');
if ($this->DownloadBehavior == null && !empty($defaultDownloadBehaviour)) {
$possibleBehaviors = $this->dbObject('DownloadBehavior')
->enumValues();
if (array_key_exists($defaultDownloadBehaviour, $possibleBehaviors)) {
$behavior = $possibleBehaviors[$defaultDownloadBehaviour];
if ($behavior) {
$this->DownloadBehavior = $behavior;
}
}
}
}
@ -1193,12 +1088,14 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
{
$extension = $this->getExtension();
$previewField = new LiteralField("ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$this->Icon($extension)}?r=" . rand(1, 100000) . "' alt='{$this->Title}' />\n"
$previewField = new LiteralField(
"ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$this->Icon($extension)}?r="
. rand(1, 100000) . "' alt='{$this->Title}' />\n"
);
//count the number of pages this document is published on
$publishedOnCount = $this->Pages()->Count();
$publishedOnCount = $this->getRelatedPages()->count();
$publishedOnValue = "$publishedOnCount pages";
if ($publishedOnCount == 1) {
$publishedOnValue = "$publishedOnCount page";
@ -1217,26 +1114,49 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
CompositeField::create(
CompositeField::create(
new ReadonlyField("ID", "ID number". ':', $this->ID),
new ReadonlyField("FileType", _t('AssetTableField.TYPE', 'File type') . ':', self::get_file_type($extension)),
new ReadonlyField("Size", _t('AssetTableField.SIZE', 'File size') . ':', $this->getFileSizeFormatted()),
$urlField = new ReadonlyField('ClickableURL', _t('AssetTableField.URL', 'URL'),
sprintf('<a href="%s" target="_blank" class="file-url">%s</a>', $this->getLink(), $this->getLink())
new ReadonlyField(
"FileType",
_t('AssetTableField.TYPE', 'File type') . ':',
self::get_file_type($extension)
),
new ReadonlyField(
"Size",
_t('AssetTableField.SIZE', 'File size') . ':',
$this->getFileSizeFormatted()
),
$urlField = new ReadonlyField(
'ClickableURL',
_t('AssetTableField.URL', 'URL'),
sprintf(
'<a href="%s" target="_blank" class="file-url">%s</a>',
$this->getLink(),
$this->getLink()
)
),
new ReadonlyField("FilenameWithoutIDField", "Filename". ':', $this->getFilenameWithoutID()),
new DateField_Disabled("Created", _t('AssetTableField.CREATED', 'First uploaded') . ':', $this->Created),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT', 'Last changed') . ':', $this->LastEdited),
new DateField_Disabled("LastChanged", _t('AssetTableField.LASTCHANGED', 'Last replaced') . ':', $this->LastChanged),
new DateField_Disabled(
"Created",
_t('AssetTableField.CREATED', 'First uploaded') . ':',
$this->Created
),
new DateField_Disabled(
"LastEdited",
_t('AssetTableField.LASTEDIT', 'Last changed') . ':',
$this->LastEdited
),
new ReadonlyField("PublishedOn", "Published on". ':', $publishedOnValue),
new ReadonlyField("ReferencedOn", "Referenced on". ':', $relationListCountValue),
new ReadonlyField("ViewCount", "View count". ':', $this->ViewCount)
)
)->setName('FilePreviewDataFields')
)->setName("FilePreviewData")->addExtraClass('cms-file-info-data')
)->setName("FilePreview")->addExtraClass('cms-file-info')
);
$fields->setName('FileP');
$fields->addExtraClass('dmsdocument-documentdetails');
$urlField->dontEscape = true;
$this->extend('updateFieldsForFile', $fields);
return $fields;
}
@ -1246,7 +1166,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
*
* @param File $file
*
* @return DMSDocument
* @return $this
*/
public function ingestFile($file)
{
@ -1255,162 +1175,192 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
return $this;
}
}
/**
* @package dms
*/
class DMSDocument_Controller extends Controller
{
public static $testMode = false; //mode to switch for testing. Does not return document download, just document URL
private static $allowed_actions = array(
'index'
);
public function init()
/**
* Get a data list of documents related to this document
*
* @return DataList
*/
public function getRelatedDocuments()
{
Versioned::choose_site_stage();
parent::init();
$documents = $this->RelatedDocuments();
$this->extend('updateRelatedDocuments', $documents);
return $documents;
}
/**
* Returns the document object from the request object's ID parameter.
* Returns null, if no document found
* @return DMSDocument|null
* Get a list of related pages for this document by going through the associated document sets
*
* @return ArrayList
*/
protected function getDocumentFromID($request)
public function getRelatedPages()
{
$doc = null;
$pages = ArrayList::create();
$id = Convert::raw2sql($request->param('ID'));
if (strpos($id, 'version') === 0) { //versioned document
$id = str_replace('version', '', $id);
$doc = DataObject::get_by_id('DMSDocument_versions', $id);
$this->extend('updateVersionFromID', $doc, $request);
} else { //normal document
$doc = DataObject::get_by_id('DMSDocument', $id);
$this->extend('updateDocumentFromID', $doc, $request);
foreach ($this->Sets() as $documentSet) {
/** @var DocumentSet $documentSet */
$pages->add($documentSet->Page());
}
$pages->removeDuplicates();
return $doc;
$this->extend('updateRelatedPages', $pages);
return $pages;
}
/**
* Access the file download without redirecting user, so we can block direct
* access to documents.
* Get a GridField for managing related documents
*
* @return GridField
*/
public function index(SS_HTTPRequest $request)
protected function getRelatedDocumentsGridField()
{
$doc = $this->getDocumentFromID($request);
$gridField = GridField::create(
'RelatedDocuments',
_t('DMSDocument.RELATEDDOCUMENTS', 'Related Documents'),
$this->RelatedDocuments(),
new GridFieldConfig_RelationEditor
);
if (!empty($doc)) {
$canView = false;
$gridFieldConfig = $gridField->getConfig();
$gridFieldConfig->removeComponentsByType('GridFieldEditButton');
$gridFieldConfig->addComponent(new DMSGridFieldEditButton(), 'GridFieldDeleteAction');
// Runs through all pages that this page links to and sets canView
// to true if the user can view ONE of these pages
if (method_exists($doc, 'Pages')) {
$pages = $doc->Pages();
if ($pages->Count() > 0) {
foreach ($pages as $page) {
if ($page->CanView()) {
// just one canView is enough to know that we can
// view the file
$canView = true;
break;
}
}
} else {
// if the document isn't on any page, then allow viewing of
// the document (because there is no canView() to consult)
$canView = true;
}
}
$gridField->getConfig()->removeComponentsByType('GridFieldAddNewButton');
// Move the autocompleter to the left
$gridField->getConfig()->removeComponentsByType('GridFieldAddExistingAutocompleter');
$gridField->getConfig()->addComponent(
$addExisting = new GridFieldAddExistingAutocompleter('buttons-before-left')
);
// check for embargo or expiry
if ($doc->isHidden()) {
$canView = false;
}
// Ensure that current document doesn't get returned in the autocompleter
$addExisting->setSearchList($this->getRelatedDocumentsForAutocompleter());
//admins can always download any document, even if otherwise hidden
$member = Member::currentUser();
if ($member && Permission::checkMember($member, 'ADMIN')) {
$canView = true;
}
// Restrict search fields to specific fields only
$addExisting->setSearchFields(array('Title:PartialMatch', 'Filename:PartialMatch'));
$addExisting->setResultsFormat('$Filename');
if ($canView) {
$path = $doc->getFullPath();
if (is_file($path)) {
$fileBin = trim(`whereis file`);
if (function_exists('finfo_file')) {
// discover the mime type properly
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $path);
} elseif (is_executable($fileBin)) {
// try to use the system tool
$mime = `$fileBin -i -b $path`;
$mime = explode(';', $mime);
$mime = trim($mime[0]);
} else {
// make do with what we have
$ext = $doc->getExtension();
if ($ext =='pdf') {
$mime = 'application/pdf';
} elseif ($ext == 'html' || $ext =='htm') {
$mime = 'text/html';
} else {
$mime = 'application/octet-stream';
}
}
if (self::$testMode) {
return $path;
}
// set fallback if no config nor file-specific value
$disposition = 'attachment';
// file-specific setting
if ($doc->DownloadBehavior == 'open') {
$disposition = 'inline';
}
//if a DMSDocument can be downloaded and all the permissions/privileges has passed,
//its ViewCount should be increased by 1 just before the browser sending the file to front.
$doc->trackView();
$this->sendFile($path, $mime, $doc->getFilenameWithoutID(), $disposition);
return;
}
}
}
if (self::$testMode) {
return 'This asset does not exist.';
}
$this->httpError(404, 'This asset does not exist.');
$this->extend('updateRelatedDocumentsGridField', $gridField);
return $gridField;
}
/**
* @param string $path File path
* @param string $mime File mime type
* @param string $name File name
* @param string $disposition Content dispositon
* Get the list of documents to show in "related documents". This can be modified via the extension point, for
* example if you wanted to exclude embargoed documents or something similar.
*
* @return DataList
*/
protected function sendFile($path, $mime, $name, $disposition) {
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($path), null);
if (!empty($mime) && $mime != "text/html") {
header('Content-Disposition: '.$disposition.'; filename="'.addslashes($name).'"');
protected function getRelatedDocumentsForAutocompleter()
{
$documents = DMSDocument::get()->exclude('ID', $this->ID);
$this->extend('updateRelatedDocumentsForAutocompleter', $documents);
return $documents;
}
/**
* Checks at least one group is selected if CanViewType || CanEditType == 'OnlyTheseUsers'
*
* @return ValidationResult
*/
protected function validate()
{
$valid = parent::validate();
if ($this->CanViewType == 'OnlyTheseUsers' && !$this->ViewerGroups()->count()) {
$valid->error(
_t(
'DMSDocument.VALIDATIONERROR_NOVIEWERSELECTED',
"Selecting 'Only these people' from a viewers list needs at least one group selected."
)
);
}
header('Content-transfer-encoding: 8bit');
header('Expires: 0');
header('Pragma: cache');
header('Cache-Control: private');
flush();
readfile($path);
exit;
if ($this->CanEditType == 'OnlyTheseUsers' && !$this->EditorGroups()->count()) {
$valid->error(
_t(
'DMSDocument.VALIDATIONERROR_NOEDITORSELECTED',
"Selecting 'Only these people' from a editors list needs at least one group selected."
)
);
}
return $valid;
}
/**
* Returns a reason as to why this document cannot be viewed.
*
* @return string
*/
public function getPermissionDeniedReason()
{
$result = '';
if ($this->CanViewType == 'LoggedInUsers') {
$result = _t('DMSDocument.PERMISSIONDENIEDREASON_LOGINREQUIRED', 'Please log in to view this document');
}
if ($this->CanViewType == 'OnlyTheseUsers') {
$result = _t(
'DMSDocument.PERMISSIONDENIEDREASON_NOTAUTHORISED',
'You are not authorised to view this document'
);
}
return $result;
}
/**
* Add an "action panel" task
*
* @param string $panelKey
* @param string $title
* @return $this
*/
public function addActionPanelTask($panelKey, $title)
{
$this->actionTasks[$panelKey] = $title;
return $this;
}
/**
* Returns a HTML representation of the action tasks for the CMS
*
* @return string
*/
public function getActionTaskHtml()
{
$html = '<div class="field dmsdocment-actions">'
. '<label class="left">' . _t('DMSDocument.ACTIONS_LABEL', 'Actions') . '</label>'
. '<ul>';
foreach ($this->actionTasks as $panelKey => $title) {
$panelKey = Convert::raw2xml($panelKey);
$title = Convert::raw2xml($title);
$html .= '<li class="ss-ui-button dmsdocument-action" data-panel="' . $panelKey . '">'
. _t('DMSDocument.ACTION_' . strtoupper($panelKey), $title)
. '</li>';
}
$html .= '</ul></div>';
return $html;
}
/**
* Removes an "action panel" tasks
*
* @param string $panelKey
* @return $this
*/
public function removeActionPanelTask($panelKey)
{
if (array_key_exists($panelKey, $this->actionTasks)) {
unset($this->actionTasks[$panelKey]);
}
return $this;
}
}

View File

@ -0,0 +1,419 @@
<?php
/**
* A document set is attached to Pages, and contains many DMSDocuments
*
* @property Varchar Title
* @property Text KeyValuePairs
* @property Enum SortBy
* @property Enum SortByDirection
*/
class DMSDocumentSet extends DataObject
{
private static $db = array(
'Title' => 'Varchar(255)',
'KeyValuePairs' => 'Text',
'SortBy' => "Enum('LastEdited,Created,Title')')",
'SortByDirection' => "Enum('DESC,ASC')')",
);
private static $has_one = array(
'Page' => 'SiteTree',
);
private static $many_many = array(
'Documents' => 'DMSDocument',
);
private static $many_many_extraFields = array(
'Documents' => array(
// Flag indicating if a document was added directly to a set - in which case it is set - or added
// via the query-builder.
'ManuallyAdded' => 'Boolean(1)',
'DocumentSort' => 'Int'
),
);
private static $summary_fields = array(
'Title' => 'Title',
'Documents.Count' => 'No. Documents'
);
/**
* Retrieve a list of the documents in this set. An extension hook is provided before the result is returned.
*
* You can attach an extension to this event:
*
* <code>
* public function updateDocuments($document)
* {
* // do something
* }
* </code>
*
* @return DataList|null
*/
public function getDocuments()
{
$documents = $this->Documents();
$this->extend('updateDocuments', $documents);
return $documents;
}
/**
* Put the "documents" list into the main tab instead of its own tab, and replace the default "Add Document" button
* with a customised button for DMS documents
*
* @return FieldList
*/
public function getCMSFields()
{
// PHP 5.3 only
$self = $this;
$this->beforeUpdateCMSFields(function (FieldList $fields) use ($self) {
$fields->removeFieldsFromTab(
'Root.Main',
array('KeyValuePairs', 'SortBy', 'SortByDirection')
);
// Don't put the GridField for documents in until the set has been created
if (!$self->isInDB()) {
$fields->addFieldToTab(
'Root.Main',
LiteralField::create(
'GridFieldNotice',
'<p class="message warning">' . _t(
'DMSDocumentSet.GRIDFIELD_NOTICE',
'Managing documents will be available once you have created this document set.'
) . '</p>'
),
'Title'
);
} else {
$fields->removeByName('DocumentSetSort');
// Document listing
$gridFieldConfig = GridFieldConfig::create()
->addComponents(
new GridFieldButtonRow('before'),
new GridFieldToolbarHeader(),
new GridFieldFilterHeader(),
new GridFieldSortableHeader(),
new GridFieldDataColumns(),
new DMSGridFieldEditButton(),
// Special delete dialog to handle custom behaviour of unlinking and deleting
new GridFieldDeleteAction(true),
new GridFieldDetailForm()
);
if (class_exists('GridFieldPaginatorWithShowAll')) {
$paginatorComponent = new GridFieldPaginatorWithShowAll(15);
} else {
$paginatorComponent = new GridFieldPaginator(15);
}
$gridFieldConfig->addComponent($paginatorComponent);
if (class_exists('GridFieldSortableRows')) {
$gridFieldConfig->addComponent(new GridFieldSortableRows('DocumentSort'));
} elseif (class_exists('GridFieldOrderableRows')) {
$gridFieldConfig->addComponent(new GridFieldOrderableRows('DocumentSort'));
}
// Don't show which page this is if we're already editing within a page context
if (Controller::curr() instanceof CMSPageEditController) {
$fields->removeByName('PageID');
} else {
$fields->fieldByName('Root.Main.PageID')->setTitle(_t('DMSDocumentSet.SHOWONPAGE', 'Show on page'));
}
// Don't show which page this is if we're already editing within a page context
if (Controller::curr() instanceof CMSPageEditController) {
$fields->removeByName('PageID');
} else {
$fields->fieldByName('Root.Main.PageID')->setTitle(_t('DMSDocumentSet.SHOWONPAGE', 'Show on page'));
}
$gridFieldConfig->getComponentByType('GridFieldDataColumns')
->setDisplayFields($self->getDocumentDisplayFields())
->setFieldCasting(array('LastEdited' => 'Datetime->Ago'))
->setFieldFormatting(
array(
'FilenameWithoutID' => '<a target=\'_blank\' class=\'file-url\''
. ' href=\'$Link\'>$FilenameWithoutID</a>',
'ManuallyAdded' => function ($value) {
if ($value) {
return _t('DMSDocumentSet.MANUAL', 'Manually');
}
return _t('DMSDocumentSet.QUERYBUILDER', 'Query Builder');
}
)
);
// Override delete functionality with this class
$gridFieldConfig->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('DMSGridFieldDetailForm_ItemRequest');
$gridField = GridField::create(
'Documents',
false,
$self->Documents(),
$gridFieldConfig
);
$gridField->setModelClass('DMSDocument');
$gridField->addExtraClass('documents');
$gridFieldConfig->addComponent(
$addNewButton = new DMSGridFieldAddNewButton('buttons-before-left'),
'GridFieldExportButton'
);
$addNewButton->setDocumentSetId($self->ID);
$fields->removeByName('Documents');
$fields->addFieldsToTab(
'Root.Main',
array(
$gridField,
HiddenField::create('DMSShortcodeHandlerKey', false, DMS::inst()->getShortcodeHandlerKey())
)
);
$self->addQueryFields($fields);
}
});
$this->addRequirements();
return parent::getCMSFields();
}
/**
* Add required CSS and Javascript requirements for managing documents
*
* @return $this
*/
protected function addRequirements()
{
// Javascript to customize the grid field for the DMS document (overriding entwine
// in FRAMEWORK_DIR.'/javascript/GridField.js'
Requirements::javascript(DMS_DIR . '/javascript/DMSGridField.js');
Requirements::css(DMS_DIR . '/dist/css/dmsbundle.css');
// Javascript for the link editor pop-up in TinyMCE
Requirements::javascript(DMS_DIR . '/javascript/DocumentHtmlEditorFieldToolbar.js');
return $this;
}
/**
* Adds the query fields to build the document logic to the DMSDocumentSet.
*
* @param FieldList $fields
*/
public function addQueryFields($fields)
{
/** @var DMSDocument $doc */
$doc = singleton('DMSDocument');
/** @var FormField $field */
$dmsDocFields = $doc->scaffoldSearchFields(array('fieldClasses' => true));
$membersMap = Member::get()->map('ID', 'Name')->toArray();
asort($membersMap);
foreach ($dmsDocFields as $field) {
if ($field instanceof ListboxField) {
$map = ($field->getName() === 'Tags__ID') ? $doc->getAllTagsMap() : $membersMap;
$field->setMultiple(true)->setSource($map);
if ($field->getName() === 'Tags__ID') {
$field->setRightTitle(
_t(
'DMSDocumentSet.TAGS_RIGHT_TITLE',
'Tags can be set in the taxonomy area, and can be assigned when editing a document.'
)
);
}
}
}
$keyValPairs = DMSJsonField::create('KeyValuePairs', $dmsDocFields->toArray());
// Now lastly add the sort fields
$sortedBy = FieldGroup::create('SortedBy', array(
DropdownField::create('SortBy', '', array(
'LastEdited' => 'Last changed',
'Created' => 'Created',
'Title' => 'Document title',
), 'LastEdited'),
DropdownField::create(
'SortByDirection',
'',
array(
'DESC' => _t('DMSDocumentSet.DIRECTION_DESCENDING', 'Descending'),
'ASC' => _t('DMSDocumentSet.DIRECTION_ASCENDING', 'Ascending')
),
'DESC'
),
));
$sortedBy->setTitle(_t('DMSDocumentSet.SORTED_BY', 'Sort the document set by:'));
$fields->addFieldsToTab(
'Root.QueryBuilder',
array(
LiteralField::create(
'GridFieldNotice',
'<p class="message warning">' . _t(
'DMSDocumentSet.QUERY_BUILDER_NOTICE',
'The query builder provides the ability to add documents to a document set based on the ' .
'filters below. Please note that the set will be built using this criteria when you save the ' .
'form. This set will not be dynamically updated (see the documentation for more information).'
) . '</p>'
),
$keyValPairs,
$sortedBy
)
);
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
$this->saveLinkedDocuments();
}
/**
* Retrieve a list of the documents in this set. An extension hook is provided before the result is returned.
*/
public function saveLinkedDocuments()
{
if (empty($this->KeyValuePairs) || !$this->isChanged('KeyValuePairs')) {
return;
}
$keyValuesPair = Convert::json2array($this->KeyValuePairs);
/** @var DMSDocument $dmsDoc */
$dmsDoc = singleton('DMSDocument');
$context = $dmsDoc->getDefaultSearchContext();
$sortBy = $this->SortBy ? $this->SortBy : 'LastEdited';
$sortByDirection = $this->SortByDirection ? $this->SortByDirection : 'DESC';
$sortedBy = sprintf('%s %s', $sortBy, $sortByDirection);
/** @var DataList $documents */
$documents = $context->getResults($keyValuesPair, $sortedBy);
$documents = $this->addEmbargoConditions($documents);
$documents = $this->addQueryBuilderSearchResults($documents);
}
/**
* Add embargo date conditions to a search query
*
* @param DataList $documents
* @return DataList
*/
protected function addEmbargoConditions(DataList $documents)
{
$now = SS_Datetime::now()->Rfc2822();
return $documents->where(
"\"EmbargoedIndefinitely\" = 0 AND "
. " \"EmbargoedUntilPublished\" = 0 AND "
. "(\"EmbargoedUntilDate\" IS NULL OR "
. "(\"EmbargoedUntilDate\" IS NOT NULL AND '{$now}' >= \"EmbargoedUntilDate\")) AND "
. "\"ExpireAtDate\" IS NULL OR (\"ExpireAtDate\" IS NOT NULL AND '{$now}' < \"ExpireAtDate\")"
);
}
/**
* Remove all ManuallyAdded = 0 original results and add in the new documents returned by the search context
*
* @param DataList $documents
* @return DataList
*/
protected function addQueryBuilderSearchResults(DataList $documents)
{
/** @var ManyManyList $originals Documents that belong to just this set. */
$originals = $this->Documents();
$originals->removeByFilter('"ManuallyAdded" = 0');
foreach ($documents as $document) {
$originals->add($document, array('ManuallyAdded' => 0));
}
return $originals;
}
/**
* Customise the display fields for the documents GridField
*
* @return array
*/
public function getDocumentDisplayFields()
{
return array_merge(
(array) DMSDocument::create()->config()->get('display_fields'),
array('ManuallyAdded' => _t('DMSDocumentSet.ADDEDMETHOD', 'Added'))
);
}
protected function validate()
{
$result = parent::validate();
if (!$this->getTitle()) {
$result->error(_t('DMSDocumentSet.VALIDATION_NO_TITLE', '\'Title\' is required.'));
}
return $result;
}
public function canView($member = null)
{
$extended = $this->extendedCan(__FUNCTION__, $member);
if ($extended !== null) {
return $extended;
}
return $this->getGlobalPermission($member);
}
public function canCreate($member = null)
{
$extended = $this->extendedCan(__FUNCTION__, $member);
if ($extended !== null) {
return $extended;
}
return $this->getGlobalPermission($member);
}
public function canEdit($member = null)
{
$extended = $this->extendedCan(__FUNCTION__, $member);
if ($extended !== null) {
return $extended;
}
return $this->getGlobalPermission($member);
}
public function canDelete($member = null)
{
$extended = $this->extendedCan(__FUNCTION__, $member);
if ($extended !== null) {
return $extended;
}
return $this->getGlobalPermission($member);
}
/**
* Checks if a then given (or logged in) member is either an ADMIN, SITETREE_EDIT_ALL or has access
* to the DMSDocumentAdmin module, in which case permissions is granted.
*
* @param Member $member
* @return bool
*/
public function getGlobalPermission(Member $member = null)
{
if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
$member = Member::currentUser();
}
$result = ($member &&
Permission::checkMember(
$member,
array('ADMIN', 'SITETREE_EDIT_ALL', 'CMS_ACCESS_DMSDocumentAdmin')
)
);
return (bool) $result;
}
}

View File

@ -0,0 +1,150 @@
<?php
class DMSDocument_Controller extends Controller
{
/**
* Mode to switch for testing. Does not return document download, just document URL.
*
* @var boolean
*/
public static $testMode = false;
private static $allowed_actions = array(
'index'
);
public function init()
{
Versioned::choose_site_stage();
parent::init();
}
/**
* Returns the document object from the request object's ID parameter.
* Returns null, if no document found
*
* @param SS_HTTPRequest $request
* @return DMSDocument|null
*/
protected function getDocumentFromID($request)
{
$doc = null;
$id = Convert::raw2sql($request->param('ID'));
if (strpos($id, 'version') === 0) {
// Versioned document
$id = $this->getDocumentIdFromSlug(str_replace('version', '', $id));
$doc = DataObject::get_by_id('DMSDocument_versions', $id);
$this->extend('updateVersionFromID', $doc, $request);
} else {
// Normal document
$doc = DataObject::get_by_id('DMSDocument', $this->getDocumentIdFromSlug($id));
$this->extend('updateDocumentFromID', $doc, $request);
}
return $doc;
}
/**
* Get a document's ID from a "friendly" URL slug containing a numeric ID and slugged title
*
* @param string $slug
* @return int
* @throws InvalidArgumentException if an invalid format is provided
*/
protected function getDocumentIdFromSlug($slug)
{
$parts = (array) sscanf($slug, '%d-%s');
$id = array_shift($parts);
if (is_numeric($id)) {
return (int) $id;
}
throw new InvalidArgumentException($slug . ' is not a valid DMSDocument URL');
}
/**
* Access the file download without redirecting user, so we can block direct
* access to documents.
*/
public function index(SS_HTTPRequest $request)
{
$doc = $this->getDocumentFromID($request);
if (!empty($doc)) {
$canView = $doc->canView();
if ($canView) {
$path = $doc->getFullPath();
if (is_file($path)) {
$fileBin = trim(`whereis file`);
if (function_exists('finfo_file')) {
// discover the mime type properly
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $path);
} elseif (is_executable($fileBin)) {
$path = escapeshellarg($path);
// try to use the system tool
$mime = `$fileBin -i -b $path`;
$mime = explode(';', $mime);
$mime = trim($mime[0]);
} else {
// make do with what we have
$ext = $doc->getExtension();
if ($ext == 'pdf') {
$mime = 'application/pdf';
} elseif ($ext == 'html' || $ext =='htm') {
$mime = 'text/html';
} else {
$mime = 'application/octet-stream';
}
}
if (self::$testMode) {
return $path;
}
// set fallback if no config nor file-specific value
$disposition = 'attachment';
// file-specific setting
if ($doc->DownloadBehavior == 'open') {
$disposition = 'inline';
}
//if a DMSDocument can be downloaded and all the permissions/privileges has passed,
//its ViewCount should be increased by 1 just before the browser sending the file to front.
$doc->trackView();
return $this->sendFile($path, $mime, $doc->getFilenameWithoutID(), $disposition);
}
}
}
if (self::$testMode) {
return 'This asset does not exist.';
}
$this->httpError(404, 'This asset does not exist.');
}
/**
* @param string $path File path
* @param string $mime File mime type
* @param string $name File name
* @param string $disposition Content dispositon
*/
protected function sendFile($path, $mime, $name, $disposition)
{
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($path), null);
if (!empty($mime) && $mime != "text/html") {
header('Content-Disposition: '.$disposition.'; filename="'.addslashes($name).'"');
}
header('Content-transfer-encoding: 8bit');
header('Expires: 0');
header('Pragma: cache');
header('Cache-Control: private');
flush();
readfile($path);
exit;
}
}

View File

@ -34,7 +34,7 @@ class DMSDocument_versions extends DataObject
private static $display_fields = array(
'VersionCounter' => 'Version Counter',
'FilenameWithoutID' => 'Filename',
'LastChanged' => 'Last Changed'
'LastEdited' => 'Last Changed'
);
private static $summary_fields = array(
@ -47,7 +47,7 @@ class DMSDocument_versions extends DataObject
);
private static $default_sort = array(
'LastChanged' => 'DESC'
'LastEdited' => 'DESC'
);
@ -179,7 +179,7 @@ class DMSDocument_versions extends DataObject
if (!$filename) {
$filename = $this->Filename;
}
return DMS::get_dms_path() . DIRECTORY_SEPARATOR . $this->Folder . DIRECTORY_SEPARATOR . $filename;
return DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $this->Folder . DIRECTORY_SEPARATOR . $filename;
}
/**
@ -206,14 +206,17 @@ class DMSDocument_versions extends DataObject
$filename = $doc->Filename;
do {
$versionPaddingString = str_pad($versionCounter, 4, '0', STR_PAD_LEFT); //add leading zeros to make sorting accurate up to 10,000 documents
// Add leading zeros to make sorting accurate up to 10,000 documents
$versionPaddingString = str_pad($versionCounter, 4, '0', STR_PAD_LEFT);
$newVersionFilename = preg_replace('/([0-9]+~)(.*?)/', '$1~'.$versionPaddingString.'~$2', $filename);
if ($newVersionFilename == $filename || empty($newVersionFilename)) { //sanity check for crazy document names
// Sanity check for crazy document names
if ($newVersionFilename == $filename || empty($newVersionFilename)) {
user_error('Cannot generate new document filename for file: '.$filename, E_USER_ERROR);
}
$versionCounter++; //increase the counter for the next loop run, if necessary
// Increase the counter for the next loop run, if necessary
$versionCounter++;
} while (file_exists($this->getFullPath($newVersionFilename)));
return $newVersionFilename;

View File

@ -1,20 +0,0 @@
<?php
/**
* Hold a set of metadata category/value tags associated with a DMSDocument
*
* @package dms
*/
class DMSTag extends DataObject
{
private static $db = array(
'Category' => 'Varchar(1024)',
'Value' => 'Varchar(1024)',
'MultiValue' => 'Boolean(1)'
);
private static $belongs_many_many = array(
'Documents' => 'DMSDocument'
);
}

View File

@ -0,0 +1,249 @@
<?php
/**
* This build task helps to migrate DMS data structures from DMS 1.x to 2.x which introduces document sets.
*
* See the "document-sets.md" migration guide for more information and use examples.
*/
class MigrateToDocumentSetsTask extends BuildTask
{
protected $title = 'DMS 2.0 Migration Tool';
protected $description = 'Migration tool for upgrading from DMS 1.x to 2.x. Add "action=create-default-document-set" to create a default set. "reassign-documents" to reassign legacy document relations. "dryrun=1" to show changes without writing.';
/**
* The valid actions that this task can perform (and the method that does them as the key)
* @var array
*/
protected $validActions = array(
'createDefaultSet' => 'create-default-document-set',
'reassignDocuments' => 'reassign-documents'
);
/**
* @var SS_HTTPRequest
*/
protected $request;
/**
* Holds number of pages/sets/documents processed for output at the end. Example:
*
* <code>
* array(
* 'total-pages' => 0,
* 'pages-updated' => 0
* )
* </code>
*
* The individual action methods will update these metrics as required
*
* @var array
*/
protected $results = array();
public function run($request)
{
$this->request = $request;
$action = $request->getVar('action');
if (!in_array($action, $this->validActions)) {
$this->output(
'Error! Specified action is not valid. Valid actions are: ' . implode(', ', $this->validActions)
);
$this->output('You can add "dryrun=1" to enable dryrun mode where no changes will be written to the DB.');
return;
}
$this->outputHeader();
$action = array_search($action, $this->validActions);
$this->$action();
$this->outputResults();
}
/**
* Returns whether dryrun mode is enabled ("dryrun=1")
*
* @return bool
*/
public function isDryrun()
{
return (bool) $this->request->getVar('dryrun') == 1;
}
/**
* Creates a default document set for any valid page that doesn't have one
*
* @return $this
*/
protected function createDefaultSet()
{
$pages = SiteTree::get();
foreach ($pages as $page) {
// Only handle valid page types
if (!$page->config()->get('documents_enabled')) {
$this->addResult('Skipped: documents disabled');
continue;
}
if ($page->DocumentSets()->count()) {
// Don't add a set if it already has one
$this->addResult('Skipped: already has a set');
continue;
}
$this->addDefaultDocumentSet($page);
$this->addResult('Default document set added');
}
return $this;
}
/**
* Reassign documents to the default document set, where they'd previously have been assigned to pages
*
* @return $this
*/
protected function reassignDocuments()
{
$countCheck = SQLSelect::create('*', 'DMSDocument_Pages');
if (!$countCheck->count()) {
$this->output('There was no data to migrate. Finishing.');
return $this;
}
$query = SQLSelect::create(array('DMSDocumentID', 'SiteTreeID'), 'DMSDocument_Pages');
$result = $query->execute();
foreach ($result as $row) {
$document = DMSDocument::get()->byId($row['DMSDocumentID']);
if (!$document) {
$this->addResult('Skipped: document does not exist');
continue;
}
$page = SiteTree::get()->byId($row['SiteTreeID']);
if (!$page) {
$this->addResult('Skipped: page does not exist');
continue;
}
// Don't try and process pages that don't have a document set. This should be created by the first
// action step in this build task, so shouldn't occur if run in correct order.
if (!$page->DocumentSets()->count()) {
$this->addResult('Skipped: no default document set');
continue;
}
$this->addDocumentToSet($document, $page->DocumentSets()->first());
$this->addResult('Reassigned to document set');
}
return $this;
}
/**
* Create a "default" document set and add it to the given Page via the ORM relationship added by
* {@link DMSSiteTreeExtension}
*
* @param SiteTree $page
* @return $this
*/
protected function addDefaultDocumentSet(SiteTree $page)
{
if ($this->isDryrun()) {
return $this;
}
$set = DMSDocumentSet::create();
$set->Title = 'Default';
$set->write();
$page->DocumentSets()->add($set);
return $this;
}
/**
* Add the given document to the given document set
*
* @param DMSDocument $document
* @param DMSDocumentSet $set
* @return $this
*/
protected function addDocumentToSet(DMSDocument $document, DMSDocumentSet $set)
{
if ($this->isDryrun()) {
return $this;
}
$set->Documents()->add($document);
return $this;
}
/**
* Output a header info line
*
* @return $this
*/
protected function outputHeader()
{
$this->output('Migrating DMS data to 2.x for document sets');
if ($this->isDryrun()) {
$this->output('NOTE: Dryrun mode enabled. No changes will be written.');
}
return $this;
}
/**
* Output a "finished" notice and the results of what was done
*
* @return $this
*/
protected function outputResults()
{
$this->output();
$this->output('Finished:');
foreach ($this->results as $metric => $count) {
$this->output('+ ' . $metric . ': ' . $count);
}
return $this;
}
/**
* Add the $increment to the result key identified by $key
*
* @param string $key
* @param int $increment
* @return $this
*/
protected function addResult($key, $increment = 1)
{
if (!array_key_exists($key, $this->results)) {
$this->results[$key] = 0;
}
$this->results[$key] += $increment;
return $this;
}
/**
* Outputs a message formatted either for CLI or browser output
*
* @param string $message
* @return $this
*/
public function output($message = '')
{
if ($this->isCli()) {
echo $message, PHP_EOL;
} else {
echo $message . '<br />';
}
return $this;
}
/**
* Returns whether the task is called via CLI or not
*
* @return bool
*/
protected function isCli()
{
return Director::is_cli();
}
}

View File

@ -39,19 +39,22 @@ class ShortCodeRelationFinder
}
/**
* @param int $number
* @return DataList
*/
public function getList($number)
{
$number = (int) $number;
$list = DataList::create('SiteTree');
$where = array();
$fields = $this->getShortCodeFields('SiteTree');
$shortcode = DMS::inst()->getShortcodeHandlerKey();
foreach ($fields as $ancClass => $ancFields) {
foreach ($ancFields as $ancFieldName => $ancFieldSpec) {
if ($ancClass != "SiteTree") {
$list = $list->leftJoin($ancClass, '"'.$ancClass.'"."ID" = "SiteTree"."ID"');
}
$where[] = "\"$ancClass\".\"$ancFieldName\" LIKE '%[dms_document_link,id=$number]%'"; //."%s" LIKE ""',
$where[] = "\"$ancClass\".\"$ancFieldName\" LIKE '%[{$shortcode},id=$number]%'"; //."%s" LIKE ""',
}
}

View File

@ -9,13 +9,18 @@
"email": "julian@silverstripe.com"
}],
"require": {
"silverstripe/framework": "~3.1",
"silverstripe/cms": "~3.1",
"silverstripe-australia/gridfieldextensions": "^1.1.0"
"silverstripe/framework": "^3.5",
"silverstripe/cms": "^3.5",
"symbiote/silverstripe-gridfieldextensions": "^2.0",
"silverstripe/taxonomy": "^1.2",
"mustangostang/spyc": "<=0.6.2"
},
"suggest": {
"undefinedoffset/sortablegridfield": "Allow documents to be reordered via drag-and-drop"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-master": "2.4.x-dev"
}
}
}

View File

@ -1,20 +0,0 @@
# Require any additional compass plugins here.
require 'compass-colors'
project_type = :stand_alone
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "css"
sass_dir = "scss"
images_dir = "images"
javascripts_dir = "javascript"
output_style = :compact
# To enable relative paths to assets via compass helper functions. Uncomment:
relative_assets = true
# disable comments in the output. We want admin comments
# to be verbose
line_comments = false
asset_cache_buster :none

View File

@ -1,62 +0,0 @@
form.small .field input.text, form.small .field textarea, form.small .field select, form.small .field .TreeDropdownField, .field.small input.text, .field.small textarea, .field.small select, .field.small .TreeDropdownField { width: 100%; }
#FileP .fieldgroup-field { width: 100%; }
#FileP .fieldgroup-field .cms-file-info-preview { box-shadow: none; }
#FileP .fieldgroup-field .cms-file-info-data { width: 400px; }
#FileP .fieldgroup-field .fieldholder-small { margin-top: 5px; }
#FileP .fieldgroup-field .fieldholder-small label { width: auto; margin-left: 0; padding-top: 0; margin-right: 10px; font-weight: bold; float: left; }
#FileP .fieldgroup-field .fieldholder-small .readonly { font-style: italic; color: #666; }
#DocumentTypeID .middleColumn { overflow: auto; min-width: 800px; }
#DocumentTypeID .middleColumn ul { padding: 0; }
#DocumentTypeID .middleColumn ul input[type="radio"] { display: none; }
#DocumentTypeID .middleColumn ul li { display: table; padding: 0; width: 125px; height: 40px; white-space: normal; margin-right: -1px; border-radius: 0; }
#DocumentTypeID .middleColumn ul li:first-child { border-radius: 6px 0 0 6px; border-left: 1px solid #C0C0C2; }
#DocumentTypeID .middleColumn ul li:last-child { border-radius: 0 6px 6px 0; }
#DocumentTypeID .middleColumn ul li.selected, .ie8 #DocumentTypeID .middleColumn ul li.selected { border-bottom: 1px solid #C0C0C2; background: -moz-linear-gradient(#c0c0c0, #e6e6e6); background: -webkit-linear-gradient(#c0c0c0, #e6e6e6); background: linear-gradient(#c0c0c0, #e6e6e6); background-color: silver; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */ box-shadow: 0 1px 1px 0 #A0A0A0 inset; }
#DocumentTypeID .middleColumn ul li.selected :after, .ie8 #DocumentTypeID .middleColumn ul li.selected :after { box-shadow: 0 1px 1px 0 #DDD; }
#DocumentTypeID .middleColumn ul li label { display: table-cell; vertical-align: middle; text-align: center; padding: 0 10px; }
#Actions { box-shadow: none; border: none; padding: 0; }
#Actions li { margin-left: 2px; }
#Actions li:first-child { margin-left: 0; }
#Actions li.delete-button-appended { display: inline-block; position: relative; margin-left: 0; }
#Actions li.dms-active { border-bottom: 1px solid #C0C0C2; background: -moz-linear-gradient(#c0c0c0, #e6e6e6); background: -webkit-linear-gradient(#c0c0c0, #e6e6e6); background: linear-gradient(#c0c0c0, #e6e6e6); background-color: silver; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */ box-shadow: 0 1px 1px 0 #A0A0A0 inset; }
#Actions li.dms-active:after { content: ''; display: block; position: absolute; top: 33px; left: 50%; margin-left: -6px; width: 12px; height: 12px; background: #F8F8F8; border-right: 1px solid #B3B3B3; border-top: 1px solid #B3B3B3; -moz-transform: rotate(-45deg); -webkit-transform: rotate(-45deg); pointer-events: none; z-index: 10; }
.DMSDocumentActionsPanel { /*.middleColumn{
margin-left:184px;
width: 510px;
form.small &{
margin-left: 120px;
}
}
*/ /*label.fieldholder-small-label{
display: none;
}*/ }
.DMSDocumentActionsPanel .fieldgroup { display: none; float: none; width: auto; background: #f8f8f8; padding: 15px; border: 1px solid #d0d3d5; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }
.DMSDocumentActionsPanel .fieldgroup .fieldholder-small label { float: none; width: auto; margin: 0; padding: 0; }
.DMSDocumentActionsPanel .fieldgroup .fieldholder-small label.ss-ui-button { float: left; margin: 0 10px 0 0; }
.DMSDocumentActionsPanel .fieldgroup .embargo li, .DMSDocumentActionsPanel .fieldgroup .expiry li { float: none; width: 100%; margin-left: 8px; }
.DMSDocumentActionsPanel .fieldgroup .embargo li label, .DMSDocumentActionsPanel .fieldgroup .expiry li label { padding-left: 10px; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime { margin-top: 0; margin-left: 34px; overflow: hidden; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .field.date, .DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .field.time, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .field.date, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .field.time, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .field.date, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .field.time, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .field.date, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .field.time { display: inline-block; width: auto; margin: 0; padding: 0; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .field.date .middleColumn, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .field.date .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .field.date .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .field.date .middleColumn { background: url("../images/calendar-month.png") 90px 7px no-repeat; overflow: hidden; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .field.time .middleColumn, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .field.time .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .field.time .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .field.time .middleColumn { background: url("../images/clock-frame.png") 90px 7px no-repeat; overflow: hidden; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .middleColumn, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .middleColumn, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .middleColumn { margin-left: 0; width: auto; border: none; }
.DMSDocumentActionsPanel .fieldgroup .embargo .embargoDatetime .middleColumn input, .DMSDocumentActionsPanel .fieldgroup .embargo .expiryDatetime .middleColumn input, .DMSDocumentActionsPanel .fieldgroup .expiry .embargoDatetime .middleColumn input, .DMSDocumentActionsPanel .fieldgroup .expiry .expiryDatetime .middleColumn input { width: 80px; margin-right: 40px; }
.DMSDocumentActionsPanel #Embargo { border: none; box-shadow: none; -moz-box-shadow: none; -webkit-box-shadow: none; }
.DMSDocumentActionsPanel .ss-uploadfield-files .ss-uploadfield-item-preview { background: url("../images/app_icons/generic_32.png") -10px -6px no-repeat; }
.DMSDocumentActionsPanel .ss-uploadfield-files .ss-uploadfield-item-name span.name { width: 260px; }
.DMSDocumentActionsPanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel { width: auto; text-indent: 0; }
.DMSDocumentActionsPanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel .btn-icon-deleteLight { background-position: 0 -128px; display: inline-block; }
.DMSDocumentActionsPanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel .ui-button-text { display: block; position: relative; float: right; color: #555; padding: 0; padding-left: 2em; }
.DMSDocumentActionsPanel > .fieldgroup.middleColumn { overflow: hidden; display: block; }
.DMSDocumentActionsPanel > .fieldgroup.middleColumn .fieldgroup-field { width: 100%; }
#Form_ItemEditForm fieldset table.ss-gridfield-table { width: 494px; }
#Form_ItemEditForm fieldset table.ss-gridfield-table tr th.main { min-width: 175px; }
#Form_ItemEditForm fieldset table.ss-gridfield-table tr th.main.col-action_SetOrderID { width: 60px; min-width: 60px; }
#Form_ItemEditForm fieldset table.ss-gridfield-table tr td { white-space: normal; }
#ui-datepicker-div { border: 1px solid #DDD; }

View File

@ -1,70 +0,0 @@
.ui-autocomplete { border: 1px solid #DDD; box-shadow: 0 1px 2px 0px #AFAFAF; max-height: 300px; overflow: scroll; }
.cms fieldset.documents table td, .cms fieldset.relatedLinks table td { white-space: normal; }
.cms fieldset.documents table td.col-buttons, .cms fieldset.relatedLinks table td.col-buttons { white-space: nowrap; }
.cms fieldset.documents table td.col-buttons .dms-delete-link-only .ui-icon, .cms fieldset.relatedLinks table td.col-buttons .dms-delete-link-only .ui-icon { background: url(../images/chain-unchain.png) no-repeat; }
.cms fieldset.documents table tr.dms-document-hidden-row.odd, .cms fieldset.relatedLinks table tr.dms-document-hidden-row.odd { background-color: rgba(241, 220, 222, 0.6); }
.cms fieldset.documents table tr.dms-document-hidden-row.even, .cms fieldset.relatedLinks table tr.dms-document-hidden-row.even { background-color: rgba(242, 201, 203, 0.675); }
.cms fieldset.documents table tr.dms-document-hidden-row:hover, .cms fieldset.relatedLinks table tr.dms-document-hidden-row:hover { background-color: rgba(244, 171, 172, 0.8) !important; }
.DMSDocumentAddController .ui-tabs ul.ui-tabs-nav { border-bottom: none; float: right; margin: 8px 0 -1px 0; padding: 0 24px 0 0; }
.DMSDocumentAddController .ui-tabs ul.ui-tabs-nav li { padding-bottom: 1px; border: 1px solid #C0C0C2; }
.DMSDocumentAddController .ui-tabs ul.ui-tabs-nav li a { padding: 8px 20px 8px; }
.ss-add .document-add-existing input.document-autocomplete { position: absolute; z-index: 9999; width: 390px; padding: 9px 7px; border-bottom-right-radius: 0; border-top-right-radius: 0; outline: none; box-sizing: border-box; -moz-box-sizing: border-box; }
.ss-add .document-add-existing input.document-autocomplete[disabled] { color: #C0C0C2; text-shadow: 0 -1px 0 #FFF; background: #EEE; background-image: none; box-shadow: inset 0 1px 8px 0 #C4C4C4; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.ss-add .document-add-existing .TreeDropdownField { border: none; width: 100%; max-width: 512px; box-sizing: border-box; cursor: pointer; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link { padding: 5px 9px 9px; background: #fff; border: 1px solid #B3B3B3; float: right; z-index: 99999; position: relative; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link.treedropdownfield-open-tree { background: #fff; border: 1px solid #B3B3B3; border-bottom: none; border-bottom-right-radius: 0; }
.ss-add .document-add-existing .treedropdownfield-title { width: auto; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link a { display: inline-block; top: 4px; position: relative; }
.ss-add .document-add-existing .document-list { width: 510px; border: 1px solid #DDD; border-top: none; background: #ffffff; display: none; box-shadow: 0 2px 4px 1px #DDD; max-height: 300px; border-radius: 6px; background-clip: padding-box; overflow: scroll; }
.ss-add .document-add-existing .document-list p { padding: 10px 10px 0; }
.ss-add .document-add-existing .document-list ul { padding: 4px 0; }
.ss-add .document-add-existing .document-list ul li { line-height: 18px; }
.ss-add .document-add-existing .document-list ul li a { display: block; padding: 4px 8px; border: 1px solid #FFF; color: black; }
.ss-add .document-add-existing .document-list ul li a:hover { border: 1px solid #CCC; border-radius: 4px; background: rgba(203, 203, 203, 0.4); cursor: pointer; text-decoration: none; outline: none; text-shadow: none; }
.ss-add .document-add-existing.link-editor-context label { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; }
.ss-add .document-add-existing.link-editor-context .middleColumn { margin-left: 184px; }
.ss-add .document-add-existing.link-editor-context .middleColumn input { background: white; border: 1px solid #B3B3B3; line-height: 16px; margin: 0; border-radius: 4px; background-size: 100%; max-width: 512px; }
.ss-add .ss-assetuploadfield.link-editor-context label { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; }
.ss-add .ss-assetuploadfield.link-editor-context .middleColumn { margin-left: 184px; display: block; padding: 8px 8px 8px 0; font-style: italic; min-height: 20px; }
.ss-add .ss-assetuploadfield .step4 { margin-bottom: 10px; }
.cms .ss-add .treedropdownfield-panel, .cms .selectiongroup .treedropdownfield-panel { margin: -1px 0 0 0; box-sizing: border-box; }
.cms .ss-add .treedropdownfield-panel ul, .cms .selectiongroup .treedropdownfield-panel ul { padding: 4px 0; }
.cms .ss-add .treedropdownfield-panel ul li, .cms .selectiongroup .treedropdownfield-panel ul li { border: 1px solid #ffffff; }
.cms .ss-add .treedropdownfield-panel ul li a, .cms .selectiongroup .treedropdownfield-panel ul li a { display: block; padding: 4px 2px; }
.cms .ss-add .treedropdownfield-panel ul li a.jstree-hovered, .cms .selectiongroup .treedropdownfield-panel ul li a.jstree-hovered { background: rgba(203, 203, 203, 0.4); border: 1px solid #CCC; }
@-moz-document url-prefix() { .ss-add .document-add-existing input { padding: 10px 7px; } }
#Form_EditForm_Documents { padding: 1em 0; }
#Form_EditForm_Documents input[name="filter[LastChanged]"] { display: none; }
#Form_EditForm_RelatedLinks table { padding: 1em 0; }
#Form_EditForm_RelatedLinks table thead h2 { display: none; }
#SectionID .middleColumn, #DocumentTypeID .middleColumn { overflow: auto; min-width: 800px; }
#SectionID .middleColumn ul, #DocumentTypeID .middleColumn ul { padding: 0; }
#SectionID .middleColumn ul input[type="radio"], #DocumentTypeID .middleColumn ul input[type="radio"] { display: none; }
#SectionID .middleColumn ul li, #DocumentTypeID .middleColumn ul li { display: table; padding: 0; width: 125px; height: 40px; white-space: normal; margin-right: -1px; border-radius: 0; }
#SectionID .middleColumn ul li:first-child, #DocumentTypeID .middleColumn ul li:first-child { border-radius: 6px 0 0 6px; border-left: 1px solid #C0C0C2; }
#SectionID .middleColumn ul li:last-child, #DocumentTypeID .middleColumn ul li:last-child { border-radius: 0 6px 6px 0; }
#SectionID .middleColumn ul li.selected, .ie8 #SectionID .middleColumn ul li.selected, #DocumentTypeID .middleColumn ul li.selected, .ie8 #DocumentTypeID .middleColumn ul li.selected { border-bottom: 1px solid #C0C0C2; background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c0c0c0), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#c0c0c0, #e6e6e6); background: -moz-linear-gradient(#c0c0c0, #e6e6e6); background: -o-linear-gradient(#c0c0c0, #e6e6e6); background: linear-gradient(#c0c0c0, #e6e6e6); background-color: silver; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */ box-shadow: 0 1px 1px 0 #A0A0A0 inset; }
#SectionID .middleColumn ul li.selected :after, .ie8 #SectionID .middleColumn ul li.selected :after, #DocumentTypeID .middleColumn ul li.selected :after, .ie8 #DocumentTypeID .middleColumn ul li.selected :after { box-shadow: 0 1px 1px 0 #DDD; }
#SectionID .middleColumn ul li label, #DocumentTypeID .middleColumn ul li label { display: table-cell; vertical-align: middle; text-align: center; padding: 0 10px; }
#Form_ItemEditForm h3:first-child { display: inline-block; float: left; width: 184px; }
#Form_ItemEditForm ul.SelectionGroup { display: inline-block; position: relative; padding: 0; margin-top: 9px; margin-left: 0; height: 110px; background: none; border: none; }
#Form_ItemEditForm ul.SelectionGroup li { width: auto; clear: none; display: inline; margin-right: 4px; }
#Form_ItemEditForm ul.SelectionGroup li input.selector { display: none; }
#Form_ItemEditForm ul.SelectionGroup li label.ui-button { background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -o-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); font-weight: bold; border: 1px solid #C0C0C2; border-radius: 3px; padding: 0.8em 1.5em; }
#Form_ItemEditForm ul.SelectionGroup li label.ui-button:hover { box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); }
#Form_ItemEditForm ul.SelectionGroup li div.field { margin-left: 0px; margin-bottom: 1em; width: 600px; position: absolute; left: 0; margin-top: 13px; padding: 10px; background: white; border: 1px solid #B3B3B3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -o-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
#Form_ItemEditForm ul.SelectionGroup li.selected label.ui-button { border-bottom: 1px solid #C0C0C2; background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c0c0c0), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#c0c0c0, #e6e6e6); background: -moz-linear-gradient(#c0c0c0, #e6e6e6); background: -o-linear-gradient(#c0c0c0, #e6e6e6); background: linear-gradient(#c0c0c0, #e6e6e6); background-color: silver; filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */ box-shadow: 0 1px 1px 0 #A0A0A0 inset; }
#Form_ItemEditForm ul.SelectionGroup li.selected label.ui-button:after { content: ''; display: block; position: absolute; top: 33px; left: 50%; margin-left: -6px; width: 12px; height: 12px; background: #F8F8F8; border-right: 1px solid #B3B3B3; border-top: 1px solid #B3B3B3; -moz-transform: rotate(-45deg); -webkit-transform: rotate(-45deg); pointer-events: none; z-index: 10; }
#Form_ItemEditForm ul.SelectionGroup li.selected label.ui-button:after { top: 43px; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel { margin: 1px 0 0 -1px; box-sizing: content-box; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel ul li { display: block; clear: both; width: 100%; margin: 0; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel ul li li { padding-left: 20px; }

331
dist/css/cmsbundle.css vendored Normal file
View File

@ -0,0 +1,331 @@
.dmsdocument-documentdetails .fieldgroup-field .cms-file-info-preview {
box-shadow: none; }
.dmsdocument-documentdetails .fieldgroup-field .cms-file-info-data {
width: 400px; }
.dmsdocument-documentdetails .fieldgroup-field .fieldholder-small {
margin-top: 5px; }
.dmsdocument-documentdetails .fieldgroup-field .fieldholder-small .fieldholder-small-label {
font-weight: bold;
float: left; }
.dmsdocument-documentdetails .fieldgroup-field .fieldholder-small .readonly {
font-style: italic; }
.dmsdocment-actions {
border: none;
box-shadow: none;
margin-bottom: 0;
padding: 0; }
.dmsdocment-actions label.left {
padding: 6px 0 0 0; }
.dmsdocment-actions .ss-ui-button {
border: none;
background: none;
border-radius: 0;
font-weight: normal; }
.dmsdocment-actions .ss-ui-button .ui-button-text {
padding-bottom: 1em; }
.dmsdocment-actions .ss-ui-button.dms-active, .dmsdocment-actions .ss-ui-button.dms-active:hover {
border-bottom: 4px solid #66727d; }
.dmsdocment-actions .ss-ui-button:hover, .dmsdocment-actions .ss-ui-button:active {
background: none;
border: none;
box-shadow: none; }
.dmsdocument-actionspanel {
margin-top: 0; }
.dmsdocument-actionspanel .fieldgroup {
border: 1px solid #d0d3d5;
display: none;
margin-left: 0;
padding: 5px 15px; }
.dmsdocument-actionspanel .fieldgroup .fieldholder-small label {
padding: 0; }
.dmsdocument-actionspanel .fieldgroup .embargo li,
.dmsdocument-actionspanel .fieldgroup .expiry li {
margin-left: 8px;
width: 100%; }
.dmsdocument-actionspanel .fieldgroup .embargo li label,
.dmsdocument-actionspanel .fieldgroup .expiry li label {
padding-left: 10px; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime {
float: left;
margin-top: 0;
margin-left: 34px;
overflow: hidden; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.date, .dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.time,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.date,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.time,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.date,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.time,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.date,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.time {
display: inline-block;
margin: 0;
padding: 0;
width: auto; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.date .middleColumn, .dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.time .middleColumn {
overflow: hidden; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.date .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.date .middleColumn {
background: url(../../images/calendar-month.png) 90px 7px no-repeat; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .field.time .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .field.time .middleColumn {
background: url(../../images/clock-frame.png) 90px 7px no-repeat; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .middleColumn,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .middleColumn,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .middleColumn {
border: none;
margin-left: 0;
width: auto; }
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .middleColumn input.date,
.dmsdocument-actionspanel .fieldgroup .embargo .embargoDatetime .middleColumn input.time,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .middleColumn input.date,
.dmsdocument-actionspanel .fieldgroup .embargo .expiryDatetime .middleColumn input.time,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .middleColumn input.date,
.dmsdocument-actionspanel .fieldgroup .expiry .embargoDatetime .middleColumn input.time,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .middleColumn input.date,
.dmsdocument-actionspanel .fieldgroup .expiry .expiryDatetime .middleColumn input.time {
margin-right: 40px;
width: 80px; }
.dmsdocument-actionspanel .fieldgroup .fieldgroup-field label {
margin: 0; }
.dmsdocument-actionspanel .ss-uploadfield-files .ss-uploadfield-item-preview {
background: url(../../images/app_icons/generic_32.png) -10px -6px no-repeat; }
.dmsdocument-actionspanel .ss-uploadfield-files .ss-uploadfield-item-name span.name {
width: 260px; }
.dmsdocument-actionspanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel {
text-indent: 0;
width: auto; }
.dmsdocument-actionspanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel .btn-icon-deleteLight {
background-position: 0 -128px;
display: inline-block; }
.dmsdocument-actionspanel .ss-uploadfield-files .ss-uploadfield-item-actions .ss-uploadfield-item-cancel .ui-button-text {
color: #66727d;
display: block;
float: right;
padding: 0 0 0 2em;
position: relative; }
.dmsdocument-actionspanel > .fieldgroup.middleColumn {
display: block;
overflow: hidden; }
.dmsdocument-actionspanel > .fieldgroup.middleColumn .fieldgroup-field {
width: 100%; }
.dmsdocument-actionspanel .permissions .fieldholder-small {
clear: both; }
#ui-datepicker-div {
border: 1px solid #d0d3d5; }
form.small .field input.text,
form.small .field textarea,
form.small .field select,
form.small .field .TreeDropdownField,
.field.small input.text,
.field.small textarea,
.field.small select,
.field.small .TreeDropdownField {
width: 100%; }
.dmsdocument-addexisting .ui-autocomplete {
border: 1px solid #d0d3d5;
max-height: 300px;
overflow: scroll; }
.ss-add .document-add-existing input.document-autocomplete {
position: absolute;
z-index: 9999;
width: 390px;
padding: 9px 7px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
outline: none;
box-sizing: border-box;
-moz-box-sizing: border-box; }
.ss-add .document-add-existing input.document-autocomplete[disabled] {
color: #d0d3d5;
text-shadow: 0 -1px 0 #ffffff;
background: #f8f8f8;
background-image: none;
box-shadow: inset 0 1px 8px 0 #d0d3d5;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.ss-add .document-add-existing .treedropdown {
border: none; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link {
padding: 5px 9px 9px;
background: #fff;
border: 1px solid #b3b3b3;
float: right;
z-index: 99999;
position: relative; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link.treedropdownfield-open-tree {
background: #fff;
border: 1px solid #b3b3b3;
border-bottom: none;
border-bottom-right-radius: 0; }
.ss-add .document-add-existing .treedropdownfield-title {
width: auto; }
.ss-add .document-add-existing .treedropdownfield-toggle-panel-link a {
display: inline-block;
top: 4px;
position: relative; }
.ss-add .document-add-existing .document-list {
width: 510px;
border: 1px solid #d0d3d5;
border-top: none;
background: #ffffff;
display: none;
max-height: 300px;
background-clip: padding-box;
overflow: scroll; }
.ss-add .document-add-existing .document-list p {
padding: 10px 10px 0; }
.ss-add .document-add-existing .document-list ul {
padding: 4px 0; }
.ss-add .document-add-existing .document-list ul li {
line-height: 18px; }
.ss-add .document-add-existing .document-list ul li a {
display: block;
padding: 4px 8px;
border: 1px solid #ffffff;
color: black; }
.ss-add .document-add-existing .document-list ul li a:hover {
border: 1px solid #d0d3d5;
border-radius: 4px;
background: rgba(203, 203, 203, 0.4);
cursor: pointer;
text-decoration: none;
outline: none;
text-shadow: none; }
.ss-add .document-add-existing.link-editor-context label {
float: left;
display: block;
width: 176px;
padding: 8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white; }
.ss-add .document-add-existing.link-editor-context .middleColumn input {
background: white;
border: 1px solid #b3b3b3;
line-height: 16px;
border-radius: 4px;
background-size: 100%;
max-width: 468px; }
.ss-add .document-add-existing.link-editor-context .middleColumn input.document-autocomplete {
max-width: 365px; }
.ss-add .document-add-existing.link-editor-context .document-list {
width: calc(100% - 2px); }
.ss-add .ss-assetuploadfield.link-editor-context label {
float: left;
display: block;
width: 176px;
padding: 8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white; }
.ss-add .ss-assetuploadfield.link-editor-context .middleColumn {
margin-left: 184px;
display: block;
padding: 8px 8px 8px 0;
font-style: italic;
min-height: 20px; }
.ss-add .ss-assetuploadfield .step4 {
margin-bottom: 10px; }
.cms .ss-add .treedropdownfield-panel,
.cms .selectiongroup .treedropdownfield-panel {
margin: -1px 0 0 0;
box-sizing: border-box; }
.cms .ss-add .treedropdownfield-panel ul,
.cms .selectiongroup .treedropdownfield-panel ul {
padding: 4px 0; }
.cms .ss-add .treedropdownfield-panel ul li,
.cms .selectiongroup .treedropdownfield-panel ul li {
border: 1px solid #ffffff; }
.cms .ss-add .treedropdownfield-panel ul li a,
.cms .selectiongroup .treedropdownfield-panel ul li a {
display: block;
padding: 4px 2px; }
.cms .ss-add .treedropdownfield-panel ul li a.jstree-hovered,
.cms .selectiongroup .treedropdownfield-panel ul li a.jstree-hovered {
background: rgba(203, 203, 203, 0.4);
border: 1px solid #d0d3d5; }
#Form_ItemEditForm h3:first-child {
display: inline-block;
float: left;
width: 184px; }
#Form_ItemEditForm ul.SelectionGroup {
display: inline-block;
position: relative;
padding: 0;
margin-top: 9px;
margin-left: 0;
height: 110px;
background: none;
border: none; }
#Form_ItemEditForm ul.SelectionGroup li {
width: auto;
clear: none;
display: inline;
margin-right: 4px; }
#Form_ItemEditForm ul.SelectionGroup li input.selector {
display: none; }
#Form_ItemEditForm ul.SelectionGroup li label.ui-button {
font-weight: bold;
border: 1px solid #C0C0C2;
border-radius: 3px;
padding: 0.8em 1.5em; }
#Form_ItemEditForm ul.SelectionGroup li label.ui-button:hover {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); }
#Form_ItemEditForm ul.SelectionGroup li div.field {
margin-left: 0px;
margin-bottom: 1em;
width: 600px;
position: absolute;
left: 0;
margin-top: 13px;
padding: 10px;
background: white;
border: 1px solid #b3b3b3; }
#Form_ItemEditForm ul.SelectionGroup li.selected label.ui-button:after {
top: 43px; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel {
margin: 1px 0 0 -1px;
box-sizing: content-box; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel ul li {
display: block;
clear: both;
width: 100%;
margin: 0; }
#Form_ItemEditForm ul.SelectionGroup .treedropdownfield-panel ul li li {
padding-left: 20px; }

View File

View File

@ -0,0 +1,28 @@
# Building frontend assets
This guide is intended for instructions on dealing with frontend asset files while contributing to this module. You
could also extend the Javascript and/or SCSS files using a combination of your own Webpack configurations and
`Requirements::block` calls (to block the default DMS assets), but this is not the primary intent of this document.
## Javascript
Javascript files use jQuery entwine, and live in the `javascript` folder. You can edit these files directly.
## SASS/CSS
CSS is build using Webpack and the sass-loader plugin. To install the required dependencies, you will need NodeJS and
npm installed on your local machine. You can then install by running `npm install` from the `dms` module folder.
To make changes to CSS you need to first make the change in the relevant SCSS file in the `scss` folder.
You can then compile the SCSS into CSS files:
```
npm run build
# or, to watch:
npm run watch
```
This will compile the SCSS files and produce a single compiled file under `dist/css/cmsbundle.css`. This file is named
this way to distinguish the fact that its contents are all related to the CMS rather than the frontend of a SilverStripe
website.

View File

@ -0,0 +1,46 @@
# 2.0.0 (unreleased)
## Document sets
Documents now belong to "sets", which are attached to Pages. A Page can have many Document Sets, and a Set has a
many_many relationship with Documents.
When upgrading from 1.x to 2.x you will need to migrate the relationships from your Pages to Documents to support
having a Document Set intermediary. [See here](../migration/document-sets.md) for an example build task to help with
this process.
## API changes
* `DMSSiteTreeExtension::no_documents_tab` removed, use YAML configuration `MyPage.documents_enabled: false` instead
* `DMSSiteTreeExtension::show_documents_tab` removed, use YAML configuration `MyPage.documents_enabled: true` instead
* `DMSSiteTreeExtension::PageDocuments` removed, use `DMSSiteTreeExtension::getDocumentSets` instead
* `DMSSiteTreeExtension::getDocuments` removed, use `DMSSiteTreeExtension::getAllDocuments` instead
* `DMSDocument::addPage` removed, use document sets instead
* `DMSDocument::addPages` removed, use document sets instead
* `DMSDocument::removePage` removed, use document sets instead
* `DMSDocument::removeAllPages` removed, use document sets instead
* `DMSDocument::getPages` removed, use `DMSDocument::getRelatedPages` instead
* `DMSDocumentInterface` has had the page manipulation methods removed, as above
* `DMSDocumentAddController::add_allowed_extensions` removed, use YAML configuration `DMSDocumentAddController::allowed_extensions` instead
* `DMSInterface` (and `DMS`) are stricter in the `getByPage` method, enforcing a `SiteTree` type hint
* New method `DMSInterface::getDocumentSetsByPage` (and in `DMS`)
* `DMS::$dmsFolder` removed, use YAML configuration `DMS.folder_name` instead
* `DMS::$dmsFolderSize` removed, use YAML configuration `DMS.folder_size` instead
* `DMS::get_dms_path` made non-static, use `DMS::inst()->getStoragePath()` instead
* `DMS::transform_file_to_file_path` made non-static, use `DMS::inst()->transformFileToFilePath()` instead
* `DMS::create_storage_folder` made non-static, use `DMS::inst()->createStorageFolder()` instead
* `DMS::get_storage_folder` made non-static, use `DMS::inst()->getStorageFolder()` instead
* `DMSDocument::addTag`, `::getTagsList`, `::removeTag` and `::removeAllTags` removed from the `DMSDocument` and `DMSDocumentInterface`. Please use the ORM relationship created by applying the `DMSDocumentTaxonomyExtension` extension to `DMSDocument` instead.
* `DMSInterface::getByTag` removed from `DMSInterface` and `DMS`. Use ORM relationships from applying `DMSDocumentTaxonomyExtension` to `DMSDocument` instead.
* `DMSGridFieldDeleteAction` removed
* `DMSDocument::getFileName` renamed to `DMSDocument::getFilename` for consistency
## Template changes
The default template entry point is now `DocumentSets.ss` (previously `Documents.ss`). As well as this change,
`Documents.ss` has been renamed to `DocumentSet.ss`.
## Frontend assets
We've removed the configuration for using Compass to build SCSS. You can now use Webpack instead.
[See here](building-frontend-assets.md) for more information on this.

91
docs/en/configuration.md Normal file
View File

@ -0,0 +1,91 @@
# Configuration
The file location is set via the `DMS.folder_name` configuation property, and points to a location in the webroot. By
default, this resides in an underscores folder within the assets folder. This means that automated snapshots/backups
(e.g. using [sspak](https://github.com/silverstripe/sspak)) can still handle DMS documents, but they will not show up
when navigating asset folders in the CMS.
## Changing the default storage folder
You can change the default storage folder location using YAML configuration. This folder would be relative to your
project root directory:
```yaml
DMS:
folder_name: my-custom-folder
```
## Enable/disable documents/sets for a specific page type
If you don't need documents/document sets for a specific page type you can disable this with YAML configuration:
```yaml
MyPageType:
documents_enabled: false
```
Likewise, you could override a previously set configuration value by setting this back to `true` in a configuration
file with a higher precedence.
## Allowed extensions for DMS documents
By default the allowed extensions for DMS documents will come from the UploadField's allowed extesions list, and will
have a customised list of extensions for DMS merged in. The base `allowed_extensions` is a site-wide configuration
setting. [See here for information](https://docs.silverstripe.org/en/3/developer_guides/forms/field_types/uploadfield/#limit-the-allowed-filetypes) on changing this.
To add extra allowed file extensions purely for DMS documents, you can update the YAML configuration property:
```yaml
DMSDocumentAddController:
allowed_extensions:
- php
- php5
```
## Adding fields to the Query Builder
Query builder fields are read from the DMSDocument::searchable_fields property set in [querybuilder.yml](../../_config/querybuilder.yml). Some default fields are provided and can be customised
by modifying the field and/or filter properties of a field or adding a new field entirely.
[See here for information](https://docs.silverstripe.org/en/developer_guides/model/searchfilters/) on how to modify search filters and [see here for more information](https://docs.silverstripe.org/en/developer_guides/forms/field_types/common_subclasses/)
on the field types available.
The default searchable filters available to query builder is as follows:
```yaml
DMSDocument:
searchable_fields:
Title:
title: "Document title matches ..."
Description:
title: "Document summary matches ..."
CreatedByID:
title: 'Document created by ...'
field: 'ListboxField'
filter: 'ExactMatchFilter'
LastEditedByID:
title: 'Document last changed by ...'
field: 'ListboxField'
filter: 'ExactMatchFilter'
Filename:
title: 'File name'
```
## Change the shortcode handler
If you need to change the `dms_document_link` shortcode handler for some reason, you can do so with YAML configuration
and some PHP:
```yaml
DMS:
shortcode_handler_key: your_shortcode
```
And for example in `_config.php`:
```php
ShortcodeParser::get('default')->register(
Config::inst()->get('DMS', 'shortcode_handler_key'),
array('DMSShortcodeHandler', 'handle')
);
```

View File

@ -0,0 +1,35 @@
# Creating documents
The following examples will allow you to create a DMS document in the system without associating it to a document set.
## Create by relative path
```php
$dms = DMS::inst();
$doc = $dms->storeDocument('assets/myfile.pdf');
```
## Create from an existing `File` record
```php
$dms = DMS::inst();
$file = File::get()->byID(99);
$doc = $dms->storeDocument($file);
```
Note: Both operations copy the existing file.
## Associate to a document set
If you need to associate a document to a set once it has already been created, you can use the ORM relationship from
SiteTree to access the document sets, or you can simply access the document set directly:
```php
// Add document to the first set in my page
$firstSetInPage = $myPage->DocumentSets()->first();
$firstSetInPage->add($doc);
// Add document to a specific document set
$docSet = DMSDocumentSet::get()->byId(123);
$docSet->add($doc);
```

View File

@ -0,0 +1,18 @@
# Documents on the Filesystem
While the DMS architecture allows for remote storage of files, the default implementation (the `DMS` class)
stores them locally. Relations to pages and tags are persisted as many-many relationships through the SilverStripe ORM.
File locations in this implementation are structured into subfolders, in order to avoid exceeding filesystem limits.
The file name is a composite based on its database ID and the original file name. The exact location shouldn't be
relied on by custom logic, but rather retrieved through the API method `DMSDocument::getLink`.
Example:
```
dms-assets/
0/
1234~myfile.pdf
1/
2345~myotherfile.pdf
```

View File

@ -0,0 +1,27 @@
# Download documents
## Get the download link
You can use `DMSDocument::getLink` to retrieve the secure route to download a DMS document:
```php
$document = DMSDocument::get()->byId(123);
$link = $document->getLink();
```
## Default download behaviour
The default download behaviour is "download" which will force the browser to download the document. You
can select "open" as an option in the document's settings in the CMS individually, or you can change the global
default value with configuration:
```php
Config::inst()->update('DMSDocument', 'default_download_behaviour', 'open');
```
Or in YAML:
```yaml
DMSDocument:
default_download_behaviour: open
```

20
docs/en/index.md Normal file
View File

@ -0,0 +1,20 @@
# DMS documentation
## Development
* [Configuration](configuration.md)
* [Documents on the filesystem](documents-on-the-filesystem.md)
* [Use in templates](use-in-templates.md)
* [Creating documents](creating-documents.md)
* [Download documents](download-documents.md)
* [Manage page relations](manage-page-relations.md)
* [Manage related documents](manage-related-documents.md)
* [Building frontend assets](building-frontend-assets.md)
## CMS user help
* [DMS user guide](userguide/index.md)
### Changelogs
* [2.0.0 (unreleased)](changelogs/2.0.0.md)

View File

@ -0,0 +1,35 @@
# Manage page relations
Documents are associated to pages via "document sets". You can retrieve document sets for a page, then retrieve
documents that belong to those sets. You can still retrieve all documents for a page if you want to.
## Get document sets for a page
```php
$dms = DMS::inst();
$sets = $dms->getDocumentSetsByPage($myPage);
```
You can also request sets directly from the SiteTree instance:
```php
$sets = $page->getDocumentSets();
```
## Get all related documents for a page
`DMS::getByPage` will exclude currently embargoed documents by default. To include embargoed documents as well
add `true` as the second argument.
```php
$dms = DMS::inst();
$documents = $dms->getByPage($myPage);
$documentsIncludingEmbargoed = $dms->getByPage($myPage, true);
```
You can also request this directly from the SiteTree instance:
```php
$documents = $myPage->getAllDocuments();
```

View File

@ -0,0 +1,50 @@
# Manage related documents
You can relate documents to each other using the GridField under "Related Documents" in the CMS.
## Add related documents
You can use the model relationship `DMSDocument::RelatedDocuments` to modify the DataList and save as required:
```php
$parentDocument = DMSDocument::get()->byId(123);
$relatedDocument = DMSDocument::get()->byId(234);
$parentDocument->RelatedDocuments()->add($relatedDocument);
```
Using the relationship method directly will skip the extension hook available in `getRelatedDocuments` (see below).
## Modifying the related documents list
If you need to modify the related documents DataList returned by the ORM, use the `updateRelatedDocuments` extension
hook provided by `DMSDocument::getRelatedDocuments`:
```php
# MyExtension is an extension applied to DMSDocument
class MyExtension extends DataExtension
{
public function updateRelatedDocuments($relatedDocuments)
{
foreach ($relatedDocuments as $document) {
// Add square brackets around the description
$document->Description = '[' . $document->Description . ']';
}
return $relatedDocuments;
}
}
```
## Retrieving related documents
To retrieve a DataList of related documents you can either use `getRelatedDocuments` or the ORM relationship method
`RelatedDocuments` directly. The former will allow extensions to modify the list, whereas the latter will not.
```php
$relatedDocuments = $document->getRelatedDocuments();
foreach ($relatedDocuments as $relatedDocument) {
// ...
}
```

View File

@ -0,0 +1,142 @@
# Migrating to use Document Sets
> **Warning!** Please ensure you take a backup of your database before performing any of these migration task steps.
Version 2.0.0 of the DMS module introduces document sets as the containing relationship for pages and documents. In
previous versions of DMS the relationship was between pages and documents directly.
If you are migrating from an earlier version of DMS to 2.x, you will need to set up new document sets for each page
that contained documents and establish the links from the old document-page to the new document set-document, and
document set-page.
We have included a migration build task that you can use to automate this process. It can be access via
`/dev/tasks/MigrateToDocumentSetsTask`, and will prompt you for the following steps in the migration process:
* Create a default document set for all valid pages (see note)
* Re-assign documents to their original page's new document set
## Using the migration build task
### Enabling dry run mode
For either of the "actions" in this build task, you can enable dry run mode to see what the results will be without
it actually writing anything in the database. We advise you do this as a first step.
You can enable dryrun mode by adding `dryrun=1` as an argument.
Example output will contain the following when dryrun mode is enabled:
```plain
NOTE: Dryrun mode enabled. No changes will be written.
```
### 1. Create a default document set
The first step of the migration build task will find all pages that do not have documents disabled (see note) and will
create a document set called "Default" if one does not already exist. In the case where a document set already exists
for a page, it will be used as the default.
Run from command line:
```plain
sake dev/tasks/MigrateToDocumentSetsTask action=create-default-document-set
```
Run from a browser:
```plain
http://yoursite.dev/dev/tasks/MigrateToDocumentSetsTask?action=create-default-document-set
```
An example output from this task might look like this:
```plain
Running Task DMS 2.0 Migration Tool
Migrating DMS data to 2.x for document sets
Finished:
+ Default document set added: 6
+ Skipped: documents disabled: 1
```
This task will only write records for those that are needed. If you run it more than once it will simply not do
anything.
### 2. Re-assign documents
> **Note!** If you want to choose specific document sets for documents to be assigned to rather than just the first
belonging to a page, you will need to run these queries manually (see further in this document).
The second step in the migration task is to reassign the relationship from pages to documents to document set to
documents. This task assumes that the original relationship data is still present in the database, since SilverStripe
will not remove old columns from the database tables once they've been made obsolete.
Run from command line:
```plain
sake dev/tasks/MigrateToDocumentSetsTask action=reassign-documents
```
Run from a browser:
```plain
http://yoursite.dev/dev/tasks/MigrateToDocumentSetsTask?action=reassign-documents
```
An example output from this task might look like this:
```plain
Running Task DMS 2.0 Migration Tool
Migrating DMS data to 2.x for document sets
Finished:
+ Reassigned to document set: 4
```
This task will show the same output on the initial and subsequent runs. You can follow the instructions below to clean
up legacy data after you've validated that everything is working correctly if you'd like to.
## Cleanup
Since SilverStripe will not remove the old obselete relationship table from the database, you can remove it manually
if required. Only do this once you've validated that everything has been migrated correctly.
```sql
DROP TABLE `your_ss_database`.`DMSDocument_Pages`;
```
## Migrating data manually
As mentioned earlier, if you need to migrate data manually for one reason or another you can do so with a couple of
manual SQL queries to the database.
One example of why you may need to do this is if you don't want your documents to
be automatically assigned to the "default" document set on a page, but would prefer to choose a specific set to assign
to. The automated build task cannot make this decision for us, but you can run some queries yourself.
In DMS 1.x the relationship of documents to pages is stored in the `DMSDocument_Pages` table. If you run an explain
query you will see some obviously named foreign key columns for `DMSDocumentID` and `SiteTreeID`.
In DMS 2.x the relationship is of document _sets_ to documents, and is stored in `DMSDocumentSet_Documents`.
How you manipulate this data is up to you, but an example might be that you want to move a certain range of documents
by their IDs into a specific document set (by its ID), so you could run the following:
```sql
-- Insert the new records
INSERT INTO `your_ss_database`.`DMSDocumentSet_Documents`
(`DMSDocumentSetID`, `DMSDocumentID`)
SELECT
-- your document set ID
123,
`ID`
FROM `your_ss_database`.`DMSDocument` WHERE `ID` IN(1, 2, 3, 4); -- your document IDs
```
## Notes
> Create a default document set for all valid pages
"Valid pages" means that the page class does not have the `documents_enabled` configuration property set to `false`.

View File

@ -0,0 +1,11 @@
# Use in templates
Add a simple include to any of your `.ss` templates to display the DMSDocuments associated with
the current page on the front-end.
```
<% include DocumentSets %>
```
You can fine tune the HTML markup or display behaviour of any of the templates in `/dms/templates/Includes` to change
the way documents will be displayed in your project.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -0,0 +1,3 @@
# Document Management System Cart Module
You can now request a hard-copy of preconfigured documents using the [DMS Cart Module](https://github.com/creative-commoners/silverstripe-dms-cart). The Cart Module is similar to a shopping cart and allows multiple documents to be requested in print form. This is an optional installation to enhance your DMS.

View File

@ -0,0 +1,71 @@
# Document set management
## Creating a Document Set
You can create Document Sets directly on a **Page** or in the **Documents** section.
In the **Documents** section, go to the new tab "Document Set", then use the button "Add Document Set" to create a new document set.
![Example of deleting a document set](_images/document_section.png)
If you want to create a Document Set directly from a page, navigate to the page, select the "Document Set" tab and the same "Add Document Set" button will appear.
![Example of deleting a document set](_images/pages_section.png)
Clicking the "Add Document Set" button will prompt you to fill in two fields: "Title" and "Show on page".
![Example of deleting a document set](_images/document_set.png)
The "Title" is the name of the document set that is used for display and searching purposes. The "Show on page" dropdown will let you immediately assign this document set to a page.
If you don't want to assign a document set to a page right now, you can leave it blank although the title for your new document set is compulsory. Once youve given it a name, click "Create".
## Adding documents to a Document Set
As soon as you create a Document Set you can add documents to it, regardless of where it was created from. You can do this by clicking "Add Document".
The "Add Documents" page allows you to upload a file directly from your computer; either select "Choose files to upload" if you want to find the files in your file system using a navigation tool; or just drag-and-drop the files into the "Drop Area".
Alternatively, you can add documents to your new Document Set that already exist in the CMS. To do this, just select the "From the CMS" tab and search for the filename or browse by the page that document is on.
_**Note:** All of your documents will be uploaded into the "Documents" section's "Document" tab. Uploading documents in the "Documents" section works the same way as uploading files in the "Files" section. The only difference is that DMS documents are invisible to parts of SilverStripe CMS that handle files, meaning that they cannot be selected via the "Insert File" button when editing HTML content on a page, for example. They also have a higher level of security permissions and flexibility. You can read more about_ [working with images and documents](https://userhelp.silverstripe.org/en/creating_pages_and_content/creating_and_editing_content/working_with_images_and_documents/).
After uploading your documents click "Done!". You will then be taken back to that document set. You will see your documents in a list with their ID and filename, along with other information such as when the document was last edited and how the documents were added to the document set.
**Tip:** If you have the [sortablegridfield module](http://addons.silverstripe.org/add-ons/undefinedoffset/sortablegridfield) installed, you can reorder the documents in the list by drag-and-dropping them.
![Example of deleting a document set](_images/sortablegridfield.png)
## Assigning a document set to a page
Head to the **Pages** section, click "Home" and go to the "Document Sets" tab. You can "Link existing" by using the _"Find Document Sets by Title"_ search bar to add an existing document set. To link existing document sets, start typing into the "link existing" search box and an autocompleted list of document sets that are not already assigned to a page will be displayed. Select the set you want from the results and click "Link Existing" to link it to the current page.
![Example of deleting a document set](_images/link_existing.png)
You can add and edit document sets while in the "Document Sets" tab attached to a page, or from the **Documents** section in the CMS. Both methods work the same way.
## Unlinking a document set from a page
If you want to remove a Document Set from a page, go to "Document Sets" tab and then use the "Unlink" button. This won't delete the Document Set, but will stop displaying that set on that page.
![Example of deleting a document set](_images/unlink_doc_set.png)
## Deleting a Document Set
To delete a Document Set, you must navigate to the **Documents** section. Click the "Document Set" tab and click on the delete icon on the right hand side of the set you want to delete. This will bring up a dialog box confirming if you want to delete the set. Click "OK" to delete the document set or "Cancel" to return to the Document Set page.
_**Note:** If you delete a Document Set, the documents will still remain on the system._
![Example of deleting a document set](_images/delete_doc_set.png)
## Removing a file from a Document Set
To remove a file from a Document Set, navigate to the Document Set tab (either in **Documents** or from the **Page**) and open the Document Set. All documents in that set will be displayed along with an "Unlink" button. This will not delete the document, but will remove it from the Document Set.
![Example of deleting a document set](_images/unlink_doc_from_set.png)
## Deleting files from the CMS
To delete documents entirely from the system go to the **Documents** section, "Document" tab, and click on the delete icon on the right hand side of the document you want to delete. Similarly to deleting a Document Set, you will be asked to confirm if you want to delete the document.
![Example of deleting a document set](_images/delete_doc_from_system.png)

View File

@ -0,0 +1,12 @@
---
title: Document Management System
summary: Using the Document Management System module in the SilverStripe CMS
---
# Using the Document Management System
* [Introduction to DMS](introduction.md)
* [Document set management](document-sets.md)
* [Query Builder](query-builder.md)
* [Setting permissions on a document](permissions.md)
* [Document Management System Cart Module](doc-cart.md)

View File

@ -0,0 +1,7 @@
# Introduction
The Document Management System (DMS) is a way to organise related documents into sets, either through manually creating links between documents or through utilising tags and other key criteria already placed on a Document. Sets of similar documents are called Document Sets. This module provides an easy way to create, maintain and manage Document Sets as well as individual documents included in each set.
This guide will show you how to create a Document Set manually or by using our Query Builder tool that can create a set of multiple documents with just a few clicks.
Once created, Document Sets can be added to your pages to display "Related documents", providing supporting documents or relevant information.

View File

@ -0,0 +1,47 @@
# Setting permissions on a document
You can edit a documents details and permissions inside a "Document Set" or in the **Documents** area under the "Document" tab by clicking on the document in the list. Here you will find where to change the "Title", "Description", "Cover Image", "Download behaviour" and "Actions" of your document.
There are various options that you can set for particular documents under the title "Actions".
![Example of deleting a document set](_images/actions.png)
**Embargo**
"Embargo" is where you can choose to set your content to draft then have it automatically published on a set time and date.
**Expiry**
You can also set your document to expire on a particular date under the "Expiry" tab. Expired documents will immediately stop showing up on the main website. Visitors cannot view unpublished content but you can access it in the CMS. For both "Embargo" and "Expiry" settings, simply select a date from the calendar date picker and enter the time you need.
**Replace**
The "Replace" tab allows you to replace a file using drag-and-drop or by attaching a file from your computer.
**Usage**
The "Usage" tab shows what pages the current document is used on.
**References**
The "References" tab will show you pages where the current document has been referenced in the HTML content, such as adding shortcodes to documents which direct to content.
**Related documents**
The "Related documents" tab allows you to link related documents to a document. Related documents will be displayed in a list underneath the main document when viewing the associated page on your website. This example shows "document.pdf" with its related document "example.pdf".
![Example of deleting a document set](_images/related_documents.png)
![Example of deleting a document set](_images/main_website.png)
_**Note:** You can link any document in the DMS to any document set. A document does not "belong" to a specific set. For example, if you upload a document to set-A, then relate it to set-B, then unlink the document from set-A, the document will stay on set-B._
You can set various permissions for different user groups under the "Permissions" tab such as "Who can view this document?" and "Who can edit this document?". For example, if the group "Test group" is able to view but not edit a document, the group will see a magnifying glass icon to only view uneditable information of that particular document. When viewing the main website, only users logged in and belonging to the "Test group" will be able to see the document.
![Example of deleting a document set](_images/permissions.png)
![Example of deleting a document set](_images/test_group_CMS.png)
**Versions**
The "Versions" tab shows you old versions of the document if you have previously replaced it.

View File

@ -0,0 +1,11 @@
# Query Builder
## Bulk add documents to a Document Set
The query builder is an easy way to add multiple, similar documents to a Document Set. To see the "Query Builder" tab, navigate to the Document Set you want.
![Example of deleting a document set](_images/query_builder.png)
Using a selection of criteria, query builder allows you to search for and automatically add relevant documents to the current set. The fields in this section are developer configurable, and may include things such as "title matches", "order by" and "tags match" (if the [taxonomy module](http://addons.silverstripe.org/add-ons/silverstripe/taxonomy) is installed).
Once you define query builder criteria, the results will be built when you save the document set.

BIN
images/app_icons/drawer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

View File

@ -1,125 +1,135 @@
(function($) {
"use strict";
(function ($) {
"use strict";
$.entwine('ss', function($) {
$('.document-add-existing').entwine({
adddocument: function(document_id) {
var page_id = $(this).closest('form').find(':input[name=ID]').val();
$.entwine('ss', function ($) {
$('.document-add-existing').entwine({
adddocument: function (document_id) {
var documentSetId = $(this).closest('form').find('input[name="DSID"]').val();
jQuery.ajax(
'admin/pages/adddocument/linkdocument?ID=' + page_id + '&documentID=' + document_id,
{
dataType: 'json',
success: function(data, textstatus) {
var fn = window.tmpl.cache['ss-uploadfield-addtemplate'];
var fnout = fn({
files: [data],
formatFileSize: function (bytes) {
if (typeof bytes !== 'number') return '';
if (bytes >= 1000000000) return (bytes / 1000000000).toFixed(2) + ' GB';
if (bytes >= 1000000) return (bytes / 1000000).toFixed(2) + ' MB';
return (bytes / 1000).toFixed(2) + ' KB';
}
});
jQuery.ajax(
'admin/pages/adddocument/linkdocument?dsid=' + documentSetId + '&documentID=' + document_id,
{
dataType: 'json',
success: function (data, textstatus) {
var fn = window.tmpl.cache['ss-uploadfield-addtemplate'];
var fnout = fn({
files: [data],
formatFileSize: function (bytes) {
if (typeof bytes !== 'number') {
return '';
}
if (bytes >= 1000000000) {
return (bytes / 1000000000).toFixed(2) + ' GB'
}
if (bytes >= 1000000) {
return (bytes / 1000000).toFixed(2) + ' MB';
}
return (bytes / 1000).toFixed(2) + ' KB';
}
});
$('.ss-add-files').append(fnout);
}
}
);
},
selectdocument: function(documentID, documentName) {
if (typeof(documentID) !== "undefined") {
//substitute the ID for the full document name, if no name is present
if (typeof(documentName) === "undefined") {
documentName = documentID;
}
$('.ss-add-files').html('<div class="selected-document" data-document-id="'+documentID+'">'+documentName+'</div>');
} else {
$('.ss-add-files').html('');
}
}
});
$('.ss-add-files').append(fnout);
}
}
);
},
selectdocument: function (documentID, documentName) {
if (typeof(documentID) !== "undefined") {
// Substitute the ID for the full document name, if no name is present
if (typeof(documentName) === "undefined") {
documentName = documentID;
}
$('.ss-add-files').html('<div class="selected-document" data-document-id="'+documentID+'">'+documentName+'</div>');
} else {
$('.ss-add-files').html('');
}
}
});
$('.document-add-existing .document-autocomplete').entwine({
onmatch: function() {
var self = this;
this.autocomplete({
source: 'admin/pages/adddocument/documentautocomplete',
select: function(event, ui) {
if(ui.item) {
if (self.closest('.document-add-existing').hasClass('link-editor-context')) {
$(this).closest('.document-add-existing').selectdocument(ui.item.value, ui.item.label);
} else {
$(this).closest('.document-add-existing').adddocument(ui.item.value);
}
$('.document-add-existing .document-autocomplete').entwine({
onmatch: function () {
var self = this;
this.autocomplete({
source: 'admin/pages/adddocument/documentautocomplete',
select: function (event, ui) {
if (ui.item) {
if (self.closest('.document-add-existing').hasClass('link-editor-context')) {
$(this).closest('.document-add-existing').selectdocument(ui.item.value, ui.item.label);
} else {
$(this).closest('.document-add-existing').adddocument(ui.item.value);
}
$(this).val('');
$(this).val('');
return false;
}
}
});
}
});
return false;
}
}
});
}
});
// Add label to tree drop down button
$('.document-add-existing .treedropdownfield-toggle-panel-link').entwine({
onmatch: function() {
this.prepend('<span>Browse by page</span>');
}
});
// Add label to tree drop down button
$('.document-add-existing .treedropdownfield-toggle-panel-link').entwine({
onmatch: function () {
this.prepend('<span>Browse by page</span>');
}
});
$('.document-add-existing .TreeDropdownField').entwine({
onpanelshow: function() {
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', true);
},
onpanelhide: function() {
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', $(this).closest('.document-add-existing').find('.document-list:visible').length > 0);
}
});
$('.document-add-existing .TreeDropdownField').entwine({
onpanelshow: function () {
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', true);
},
onpanelhide: function () {
$(this)
.closest('.document-add-existing')
.find('input.document-autocomplete')
.prop('disabled', $(this).closest('.document-add-existing').find('.document-list:visible').length > 0);
}
});
$('.document-add-existing input[name=PageSelector]').entwine({
onchange: function(event) {
var doclist = $(this).closest('.document-add-existing').find('.document-list');
doclist.html('<p>Loading...</p>');
doclist.show();
doclist.load(
'admin/pages/adddocument/documentlist?pageID=' + $(this).val()
);
}
});
$('.document-add-existing input[name=PageSelector]').entwine({
onchange: function (event) {
var doclist = $(this).closest('.document-add-existing').find('.document-list');
doclist.html('<p>Loading...</p>');
doclist.show();
doclist.load(
'admin/pages/adddocument/documentlist?pageID=' + $(this).val()
);
}
});
$('.document-add-existing a.add-document').entwine({
onclick: function(event) {
var document_id = this.data('document-id');
var dae = this.closest('.document-add-existing');
$('.document-add-existing a.add-document').entwine({
onclick: function (event) {
var document_id = this.data('document-id');
var dae = this.closest('.document-add-existing');
if (dae.hasClass('link-editor-context')) {
dae.selectdocument(document_id, this.text());
} else {
dae.adddocument(document_id);
}
if (dae.hasClass('link-editor-context')) {
dae.selectdocument(document_id, this.text());
} else {
dae.adddocument(document_id);
}
$(this).closest('.document-list').hide();
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', false);
$(this).closest('.document-list').hide();
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', false);
return false;
}
});
return false;
}
});
$('body').entwine({
onclick: function(event) {
$('.document-list:visible').hide()
.closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', false);
}
});
$('body').entwine({
onclick: function (event) {
$('.document-list:visible')
.hide()
.closest('.document-add-existing')
.find('input.document-autocomplete')
.prop('disabled', false);
}
});
$('.document-add-existing .treedropdownfield-toggle-panel-link').entwine({
onclick: function(event) {
$('.document-list:visible').hide();
}
});
});
}(jQuery));
$('.document-add-existing .treedropdownfield-toggle-panel-link').entwine({
onclick: function (event) {
$('.document-list:visible').hide();
}
});
});
}(jQuery));

View File

@ -1,160 +1,144 @@
(function($) {
"use strict";
"use strict";
$.entwine('ss', function($) {
$.entwine('ss', function($) {
$('#DocumentTypeID ul li').entwine({
onadd: function () {
this.addClass('ui-button ss-ui-button ui-corner-all ui-state-default ui-widget ui-button-text-only');
this.parents('ul').removeClass('ui-tabs-nav');
if (this.find('input').is(':checked')) {
this.addClass('selected');
}
},
onclick: function(e) {
$('#DocumentTypeID').find('li.selected').removeClass('selected');
this.find('input').prop("checked", true);
this.addClass('selected');
}
});
$('#DocumentTypeID ul li').entwine({
onadd: function() {
this.addClass('ui-button ss-ui-button ui-corner-all ui-state-default ui-widget ui-button-text-only');
this.parents('ul').removeClass('ui-tabs-nav');
if(this.find('input').is(':checked')) this.addClass('selected');
},
onclick: function(e){
$('#DocumentTypeID').find('li.selected').removeClass('selected');
this.find('input').prop("checked", true);
this.addClass('selected');
}
});
$('.permissions input[name="CanViewType"], .permissions input[name="CanEditType"]').entwine({
onchange: function () {
if (!this.is(':checked')) {
return;
}
/* $('#DocumentTypeID input[type=radio]').entwine({
onadd: function() {
// Checks to see what radio button is selected
if (this.is(':checked')) {
this.change();
}
},
onchange: function(e) {
// Remove selected class from radio buttons
$('#DocumentTypeID').find('li').removeClass('selected');
//If radio button is checked then add the selected class
if (this.is(':checked')) {
this.parent('li').addClass('selected');
}
}
});*/
var dropDown = this.closest('.fieldholder-small').next();
if (this.val() === 'OnlyTheseUsers') {
dropDown.removeClass('hide');
} else {
dropDown.addClass('hide');
}
},
onadd: function () {
this.trigger('change');
}
});
$('#Actions ul li').entwine({
onclick: function(e) {
$('.dmsdocment-actions ul li').entwine({
onclick: function (e) {
// Add active state to the current button
$('.dmsdocment-actions ul li').removeClass('dms-active');
this.addClass('dms-active');
//add active state to the current button
$('#Actions ul li').removeClass('dms-active');
this.addClass('dms-active');
//$('li.dms-active').append('<span class="arrow"></span>');
// Hide all inner field sections
var panel = $('.dmsdocument-actionspanel:first');
panel.find('> .fieldgroup > .fieldgroup-field').hide();
//hide all inner field sections
var panel = $('.DMSDocumentActionsPanel:first');
panel.find('> .fieldgroup > .fieldgroup-field').hide();
// Show the correct group of controls
panel.find('.'+this.data('panel')).show().parents('.fieldgroup-field').show();
}
});
//show the correct group of controls
//panel.find('.'+this.data('panel')).closest('div.fieldgroup').show();
panel.find('.'+this.data('panel')).show().parents('.fieldgroup-field').show();
$('#Form_ItemEditForm_Embargo input, #Form_EditForm_Embargo input').entwine({
onchange: function () {
// Selected the date options
if (this.attr('value') === 'Date') {
$('.embargoDatetime').children().show();
$('.embargoDatetime').show();
} else {
$('.embargoDatetime').hide();
}
}
});
}
});
$('#Form_ItemEditForm_Expiry input, #Form_EditForm_Expiry input').entwine({
onchange: function () {
// Selected the date options
if (this.attr('value') === 'Date') {
$('.expiryDatetime').children().show();
$('.expiryDatetime').show();
} else {
$('.expiryDatetime').hide();
}
}
});
$('#Form_ItemEditForm_Embargo input, #Form_EditForm_Embargo input').entwine({
onchange: function() {
console.log('called');
//selected the date options
if (this.attr('value') === 'Date') {
$('.embargoDatetime').children().show();
$('.embargoDatetime').show();
} else {
$('.embargoDatetime').hide();
}
}
});
$('.dmsdocument-actionspanel').entwine({
onadd: function () {
// Do an initial show of the entire panel
this.show();
$('#Form_ItemEditForm_Expiry input, #Form_EditForm_Expiry input').entwine({
onchange: function() {
//selected the date options
if (this.attr('value') === 'Date') {
$('.expiryDatetime').children().show();
$('.expiryDatetime').show();
} else {
$('.expiryDatetime').hide();
}
}
});
// Add some extra classes to the replace field containers to make it work with drag and drop uploading
this.find('.replace').closest('div.fieldgroup-field').addClass('ss-upload').addClass('ss-uploadfield');
$('.DMSDocumentActionsPanel').entwine({
onadd: function() {
//do an initial show of the entire panel
this.show();
// Add class and hide
$('.dmsdocument-actionspanel .embargo input.date').closest('.fieldholder-small').addClass('embargoDatetime').hide();
$('.dmsdocument-actionspanel .expiry input.date').closest('.fieldholder-small').addClass('expiryDatetime').hide();
//add some extra classes to the replace field containers to make it work with drag and drop uploading
this.find('.replace').closest('div.fieldgroup-field').addClass('ss-upload').addClass('ss-uploadfield');
// add class and hide
$('.DMSDocumentActionsPanel .embargo input.date').closest('.fieldholder-small').addClass('embargoDatetime').hide();
$('.DMSDocumentActionsPanel .expiry input.date').closest('.fieldholder-small').addClass('expiryDatetime').hide();
// We need to duplicate the above functions to work when Adding documents
// $('#Form_EditForm_EmbargoedUntilDate_date').closest('.fieldholder-small').addClass('embargoDatetime').hide();
// $('#Form_EditForm_ExpireAtDate_date').closest('.fieldholder-small').addClass('expiryDatetime').hide();
// Add placeholder attribute to date and time fields
$('.dmsdocument-actionspanel .embargo input.date').attr('placeholder', 'dd-mm-yyyy');
$('.dmsdocument-actionspanel .embargo input.time').attr('placeholder', 'hh:mm:ss');
$('.dmsdocument-actionspanel .expiry input.date').attr('placeholder', 'dd-mm-yyyy');
$('.dmsdocument-actionspanel .expiry input.time').attr('placeholder', 'hh:mm:ss');
//Add placeholder attribute to date and time fields
$('.DMSDocumentActionsPanel .embargo input.date').attr('placeholder', 'dd-mm-yyyy');
$('.DMSDocumentActionsPanel .embargo input.time').attr('placeholder', 'hh:mm:ss');
$('.DMSDocumentActionsPanel .expiry input.date').attr('placeholder', 'dd-mm-yyyy');
$('.DMSDocumentActionsPanel .expiry input.time').attr('placeholder', 'hh:mm:ss');
// We need to duplicate to work when adding documents
// $('#Form_EditForm_EmbargoedUntilDate_date').attr('placeholder', 'dd-mm-yyyy');
// $('#Form_EditForm_EmbargoedUntilDate_time').attr('placeholder', 'hh:mm:ss');
// $('#Form_EditForm_ExpireAtDate_date').attr('placeholder', 'dd-mm-yyyy');
// $('#Form_EditForm_ExpireAtDate_time').attr('placeholder', 'hh:mm:ss');
// Show the embargo panel when the page loads
$('li[data-panel="embargo"]').click();
//show the embargo panel when the page loads
$('li[data-panel="embargo"]').click();
// Set the initial state of the radio button and the associated dropdown hiding
$('.dmsdocument-actionspanel .embargo input[type="radio"][checked]').change();
$('.dmsdocument-actionspanel .expiry input[type="radio"][checked]').change();
}
});
//set the initial state of the radio button and the associated dropdown hiding
$('.DMSDocumentActionsPanel .embargo input[type="radio"][checked]').change();
$('.DMSDocumentActionsPanel .expiry input[type="radio"][checked]').change();
//Again we need to duplicate the above function to work when adding documents
// $('#Form_EditForm_Embargo input[checked]').change();
// $('#Form_EditForm_Expiry input[checked]').change();
}
});
$('#Form_ItemEditForm_action_doDelete').entwine({
onclick: function (e) {
// Work out how many pages are left attached to this document
var form = this.closest('form');
var pagesCount = form.data('pages-count');
var relationCount = form.data('relation-count');
$('#Form_ItemEditForm_action_doDelete').entwine({
onclick: function(e){
//work out how many pages are left attached to this document
var form = this.closest('form');
var pagesCount = form.data('pages-count');
var relationCount = form.data('relation-count');
// Display an appropriate message
var message = '';
if (pagesCount > 1 || relationCount > 0) {
var pages = '';
if (pagesCount > 1) {
pages = "\nWarning: doc is attached to a total of "+pagesCount+" pages. ";
}
var references = '';
var referencesWarning = '';
if (relationCount > 0) {
var pname = 'pages';
referencesWarning = "\n\nBefore deleting: please update the content on the pages where this document is referenced, otherwise the links on those pages will break.";
if (relationCount === 1) {
pname = 'page';
referencesWarning = "\n\nBefore deleting: please update the content on the page where this document is referenced, otherwise the links on that page will break.";
}
references = "\nWarning: doc is referenced in the text of "+relationCount +" "+pname+".";
}
message = "Permanently delete this document and remove it from all pages where it is referenced?\n"+pages+references+"\n\nDeleting it here will permanently delete it from this page and all other pages where it is referenced."+referencesWarning;
} else {
message = "Permanently delete this document and remove it from this page?\n\nNotice: this document is only attached to this page, so deleting it won't affect any other pages.";
}
//display an appropriate message
var message = '';
if (pagesCount > 1 || relationCount > 0) {
var pages = '';
if (pagesCount > 1) {
pages = "\nWarning: doc is attached to a total of "+pagesCount+" pages. ";
}
var references = '';
var referencesWarning = '';
if (relationCount > 0) {
var pname = 'pages';
referencesWarning = "\n\nBefore deleting: please update the content on the pages where this document is referenced, otherwise the links on those pages will break.";
if (relationCount === 1) {
pname = 'page';
referencesWarning = "\n\nBefore deleting: please update the content on the page where this document is referenced, otherwise the links on that page will break.";
}
references = "\nWarning: doc is referenced in the text of "+relationCount +" "+pname+".";
}
message = "Permanently delete this document and remove it from all pages where it is referenced?\n"+pages+references+"\n\nDeleting it here will permanently delete it from this page and all other pages where it is referenced."+referencesWarning;
} else {
message = "Permanently delete this document and remove it from this page?\n\nNotice: this document is only attached to this page, so deleting it won't affect any other pages.";
}
if(!confirm(message)) {
e.preventDefault();
return false;
} else {
//user says "okay", so go ahead and do the action
this._super(e);
}
}
});
});
}(jQuery));
if (!confirm(message)) {
e.preventDefault();
return false;
} else {
// User says "okay", so go ahead and do the action
this._super(e);
}
}
});
});
}(jQuery));

View File

@ -1,125 +1,24 @@
(function($){
"use strict";
(function ($) {
"use strict";
$.entwine('ss', function($) {
$.entwine('ss', function ($) {
$('.ss-gridfield-item a.file-url').entwine({
onclick: function (e) {
// Make sure the download link doesn't trigger a gridfield edit dialog
window.open(this.attr('href'), '_blank');
$('#SectionID ul li').entwine({
onadd: function() {
this.addClass('ui-button ss-ui-button ui-corner-all ui-state-default ui-widget ui-button-text-only');
this.parents('ul').removeClass('ui-tabs-nav');
}
});
$('#SectionID input[type=radio]').entwine({
onadd: function() {
// Checks to see what radio button is selected
if (this.is(':checked')) {
this.change();
}
},
onchange: function(e) {
// Remove selected class from radio buttons
$('#SectionID').find('li').removeClass('selected');
//If radio button is checked then add the selected class
if (this.is(':checked')) {
this.parent('li').addClass('selected');
}
}
});
$('.ss-gridfield .action.dms-delete').entwine({
onclick: function(e){
//work out how many pages are left attached to this document
var pagesCount = this.data('pages-count');
var pagesCountAfterDeletion = pagesCount - 1;
var addS = 's';
if (pagesCountAfterDeletion === 1) {
addS = '';
}
//display an appropriate message
var message = '';
if (this.hasClass('dms-delete-last-warning')) {
message = "Permanently delete this document?\n\nWarning: this document is attached only to this page, deleting it here will delete it permanently.";
}
if (this.hasClass('dms-delete-link-only')) {
message = "Unlink this document from this page?\n\nNote: it will remain attached to "+pagesCountAfterDeletion+" other page"+addS+".";
}
if(!confirm(message)) {
e.preventDefault();
return false;
} else {
//user says "okay", so go ahead and do the action
this._super(e);
}
}
});
$('.ss-gridfield .dms-document-hidden').entwine({
onadd: function() {
this.closest('tr').addClass('dms-document-hidden-row');
}
});
$('.cms-content-actions.south .ss-ui-action-destructive').entwine({
confirmBeforeDelete: function() {
var deleteButtons = $('button.dms-delete[data-pages-count=1]');
//we have page with DMSDocuments on it, and we have documents that only exist on this page
if (deleteButtons.length > 0) {
var message = "Are you sure you want to delete this page? Deleting this page will delete "+deleteButtons.length;
if (deleteButtons.length === 1) {
message += " document that is associated only with this page. This document is:\n\n";
} else {
message += " documents that are associated only with this page. These documents are:\n\n";
}
//create a list of documents and their IDs
deleteButtons.each(function(){
var tr = $(this).closest('tr');
message += tr.find('.col-ID').text() +' - '+ tr.find('.col-Title').text() +"\n";
});
if(!confirm(message)) {
return false;
}
}
return true;
}
});
$('#Form_EditForm_action_deletefromlive').entwine({
onclick: function(e) {
if (this.confirmBeforeDelete()) {
this._super(e);
} else {
return false;
}
}
});
$('#Form_EditForm_action_delete').entwine({
onclick: function(e) {
if (this.confirmBeforeDelete()) {
this._super(e);
} else {
return false;
}
}
});
$('.ss-gridfield-item a.file-url').entwine({
onclick: function(e) {
//make sure the download link doesn't trigger a gridfield edit dialog
window.open(this.attr('href'), '_blank');
e.preventDefault();
return false;
}
});
});
e.preventDefault();
return false;
}
});
$('.ss-gridfield-item a.dms-doc-sets-link').entwine({
onclick: function (e){
// Prevent the initial flash of the gridfield's edit form
e.preventDefault();
document.location.href=this.attr('href');
return false;
}
});
});
}(jQuery));

View File

@ -1,30 +1,30 @@
window.tmpl.cache['ss-uploadfield-addtemplate'] = tmpl(
'{% for (var i=0, files=o.files, l=files.length, file=files[0]; i<l; file=files[++i]) { %}' +
'<li class="ss-uploadfield-item template-download{% if (file.error) { %} ui-state-error{% } %}" data-fileid="{%=file.id%}">' +
'<div class="ss-uploadfield-item-preview preview"><span>' +
'<img src="{%=file.thumbnail_url%}" alt="" />' +
'</span></div>' +
'<div class="ss-uploadfield-item-info">' +
'<label class="ss-uploadfield-item-name">' +
'<span class="name" title="{%=file.name%}">{%=file.name%}</span> ' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-status ui-state-success-text" title="'+ss.i18n._t('UploadField.AddedToPage', 'Added to Page')+'">'+ss.i18n._t('UploadField.AddedToPage', 'Added to Page')+'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-status ui-state-error-text" title="{%=o.options.errorMessages[file.error] || file.error%}">{%=o.options.errorMessages[file.error] || file.error%}</div>' +
'{% } %}' +
'<div class="clear"><!-- --></div>' +
'</label>' +
'{% if (file.error) { %}' +
'<div class="ss-uploadfield-item-actions">' +
'<div class="ss-uploadfield-item-cancel ss-uploadfield-item-cancelfailed"><button class="icon icon-16">' + ss.i18n._t('UploadField.CANCEL', 'Cancel') + '</button></div>' +
'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-actions">{% print(file.buttons, true); %}</div>' +
'{% } %}' +
'</div>' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-editform loading"><iframe frameborder="0" src="{%=file.edit_url%}"></iframe></div>' +
'{% } %}' +
'</li>' +
'{% } %}'
);
'{% for (var i=0, files=o.files, l=files.length, file=files[0]; i<l; file=files[++i]) { %}' +
'<li class="ss-uploadfield-item template-download{% if (file.error) { %} ui-state-error{% } %}" data-fileid="{%=file.id%}">' +
'<div class="ss-uploadfield-item-preview preview"><span>' +
'<img src="{%=file.thumbnail_url%}" alt="" />' +
'</span></div>' +
'<div class="ss-uploadfield-item-info">' +
'<label class="ss-uploadfield-item-name">' +
'<span class="name" title="{%=file.name%}">{%=file.name%}</span> ' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-status ui-state-success-text" title="'+ss.i18n._t('UploadField.AddedToDocumentSet', 'Added to Document Set')+'">'+ss.i18n._t('UploadField.AddedToDocumentSet', 'Added to Document Set')+'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-status ui-state-error-text" title="{%=o.options.errorMessages[file.error] || file.error%}">{%=o.options.errorMessages[file.error] || file.error%}</div>' +
'{% } %}' +
'<div class="clear"><!-- --></div>' +
'</label>' +
'{% if (file.error) { %}' +
'<div class="ss-uploadfield-item-actions">' +
'<div class="ss-uploadfield-item-cancel ss-uploadfield-item-cancelfailed"><button class="icon icon-16">' + ss.i18n._t('UploadField.CANCEL', 'Cancel') + '</button></div>' +
'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-actions">{% print(file.buttons, true); %}</div>' +
'{% } %}' +
'</div>' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-editform loading"><iframe frameborder="0" src="{%=file.edit_url%}"></iframe></div>' +
'{% } %}' +
'</li>' +
'{% } %}'
);

View File

@ -1,27 +1,27 @@
window.tmpl.cache['ss-dmsuploadfield-downloadtemplate'] = tmpl(
'{% for (var i=0, files=o.files, l=files.length, file=files[0]; i<l; file=files[++i]) { %}' +
'<li class="ss-uploadfield-item template-download{% if (file.error) { %} ui-state-error{% } %}" data-fileid="{%=file.id%}">' +
'<div class="ss-uploadfield-item-preview preview"><span>' +
'<img src="{%=file.thumbnail_url%}" alt="" />' +
'</span></div>' +
'<div class="ss-uploadfield-item-info">' +
'<label class="ss-uploadfield-item-name">' +
'<span class="name" title="{%=file.name%}">{%=file.name%}</span> ' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-status ui-state-success-text" title="'+ss.i18n._t('UploadField.Uploaded', 'Uploaded')+'">'+ss.i18n._t('UploadField.Uploaded', 'Uploaded')+'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-status ui-state-error-text" title="{%=o.options.errorMessages[file.error] || file.error%}">{%=o.options.errorMessages[file.error] || file.error%}</div>' +
'{% } %}' +
'<div class="clear"><!-- --></div>' +
'</label>' +
'{% if (file.error) { %}' +
'<div class="ss-uploadfield-item-actions">' +
'<div class="ss-uploadfield-item-cancel ss-uploadfield-item-cancelfailed"><button class="icon icon-16">' + ss.i18n._t('UploadField.CANCEL', 'Cancel') + '</button></div>' +
'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-actions">(please click save and the page will update)</div>' +
'{% } %}' +
'</div>' +
'</li>' +
'{% } %}'
);
'{% for (var i=0, files=o.files, l=files.length, file=files[0]; i<l; file=files[++i]) { %}' +
'<li class="ss-uploadfield-item template-download{% if (file.error) { %} ui-state-error{% } %}" data-fileid="{%=file.id%}">' +
'<div class="ss-uploadfield-item-preview preview"><span>' +
'<img src="{%=file.thumbnail_url%}" alt="" />' +
'</span></div>' +
'<div class="ss-uploadfield-item-info">' +
'<label class="ss-uploadfield-item-name">' +
'<span class="name" title="{%=file.name%}">{%=file.name%}</span> ' +
'{% if (!file.error) { %}' +
'<div class="ss-uploadfield-item-status ui-state-success-text" title="'+ss.i18n._t('UploadField.Uploaded', 'Uploaded')+'">'+ss.i18n._t('UploadField.Uploaded', 'Uploaded')+'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-status ui-state-error-text" title="{%=o.options.errorMessages[file.error] || file.error%}">{%=o.options.errorMessages[file.error] || file.error%}</div>' +
'{% } %}' +
'<div class="clear"><!-- --></div>' +
'</label>' +
'{% if (file.error) { %}' +
'<div class="ss-uploadfield-item-actions">' +
'<div class="ss-uploadfield-item-cancel ss-uploadfield-item-cancelfailed"><button class="icon icon-16">' + ss.i18n._t('UploadField.CANCEL', 'Cancel') + '</button></div>' +
'</div>' +
'{% } else { %}' +
'<div class="ss-uploadfield-item-actions">(please click save and the page will update)</div>' +
'{% } %}' +
'</div>' +
'</li>' +
'{% } %}'
);

View File

@ -1,108 +1,118 @@
(function($) {
"use strict";
(function ($) {
"use strict";
$.entwine('ss', function($) {
$.entwine('ss', function ($) {
$('form.htmleditorfield-linkform input[name=LinkType]').entwine({
onchange: function (e) {
this._super(e);
$('form.htmleditorfield-linkform input[name=LinkType]').entwine({
onchange: function(e) {
this._super(e);
var form = $('form.htmleditorfield-linkform');
var show = false;
var form = $('form.htmleditorfield-linkform');
var show = false;
if (this.attr('value') === 'document') {
if (this.is(':checked')) {
show = true;
}
}
if (this.attr('value') === 'document') {
if (this.is(':checked')) {
show = true;
}
}
// Hide or show the additional document link addition tool
if (show) {
form.find('.ss-add').show();
} else {
form.find('.ss-add').hide();
}
},
onadd: function (e) {
this.change();
}
});
//hide or show the additional document link addition tool
if (show) {
form.find('.ss-add').show();
} else {
form.find('.ss-add').hide();
}
},
onadd: function(e){
this.change();
}
});
$('form.htmleditorfield-linkform').entwine({
getShortcodeKey: function () {
return this.find(':input[name=DMSShortcodeHandlerKey]').val();
},
insertLink: function () {
var href, target = null;
var checkedValue = this.find(':input[name=LinkType]:checked').val();
if (checkedValue === 'document') {
href = '[' + this.getShortcodeKey() + ',id=' + this.find('.selected-document').data('document-id') + ']';
$('form.htmleditorfield-linkform').entwine({
insertLink: function() {
var href, target = null;
var checkedValue = this.find(':input[name=LinkType]:checked').val();
if (checkedValue === 'document') {
href = '[dms_document_link,id=' + this.find('.selected-document').data('document-id') + ']';
// Determine target
if (this.find(':input[name=TargetBlank]').is(':checked')) {
target = '_blank';
}
// Determine target
if(this.find(':input[name=TargetBlank]').is(':checked')) target = '_blank';
var attributes = {
href: href,
target: target,
class: 'documentLink',
// Title is the text of the selected document
title: this.find('.selected-document').text()
};
var attributes = {
href : href,
target : target,
class : 'documentLink',
title : this.find('.selected-document').text() //title is the text of the selected document
};
this.modifySelection(function (ed) {
ed.insertLink(attributes);
});
this.modifySelection(function(ed){
ed.insertLink(attributes);
});
this.updateFromEditor();
return false;
} else {
this._super();
}
},
getCurrentLink: function () {
var selectedEl = this.getSelection(), href = "", target = "", title = "", action = "insert", style_class = "";
var linkDataSource = null;
if (selectedEl.length) {
if (selectedEl.is('a')) {
linkDataSource = selectedEl;
} else {
linkDataSource = selectedEl = selectedEl.parents('a:first');
}
}
this.updateFromEditor();
return false;
} else {
this._super();
}
},
getCurrentLink: function() {
var selectedEl = this.getSelection(), href = "", target = "", title = "", action = "insert", style_class = "";
var linkDataSource = null;
if(selectedEl.length) {
if(selectedEl.is('a')) {
linkDataSource = selectedEl;
} else {
linkDataSource = selectedEl = selectedEl.parents('a:first');
}
}
if(linkDataSource && linkDataSource.length) this.modifySelection(function(ed){
ed.selectNode(linkDataSource[0]);
});
if (linkDataSource && linkDataSource.length) {
this.modifySelection(function (ed) {
ed.selectNode(linkDataSource[0]);
});
}
// Is anchor not a link
if (!linkDataSource.attr('href')) linkDataSource = null;
// Is anchor not a link
if (!linkDataSource.attr('href')) {
linkDataSource = null;
}
if (linkDataSource) {
href = linkDataSource.attr('href');
target = linkDataSource.attr('target');
title = linkDataSource.attr('title');
style_class = linkDataSource.attr('class');
href = this.getEditor().cleanLink(href, linkDataSource);
action = "update";
}
if (linkDataSource) {
href = linkDataSource.attr('href');
target = linkDataSource.attr('target');
title = linkDataSource.attr('title');
style_class = linkDataSource.attr('class');
href = this.getEditor().cleanLink(href, linkDataSource);
action = "update";
}
//match a document or call the regular link handling
if(href.match(/^\[dms_document_link(\s*|%20|,)?id=([0-9]+)\]?$/i)) {
var returnArray = {
LinkType: 'document',
DocumentID: RegExp.$2,
Description: title
};
// Match a document or call the regular link handling
if (href.match(new RegExp('^\\[' + this.getShortcodeKey() + '(\s*|%20|,)?id=([0-9]+)\\]?$', 'i'))) {
var returnArray = {
LinkType: 'document',
DocumentID: RegExp.$2,
Description: title
};
//show the selected document
$('.document-add-existing').selectdocument(returnArray.DocumentID,returnArray.Description);
// Show the selected document
$('.document-add-existing').selectdocument(returnArray.DocumentID,returnArray.Description);
//select the correct radio button
$('form.htmleditorfield-linkform input[name=LinkType][value=document]').click();
return returnArray;
} else {
$('.document-add-existing').selectdocument(); //clear the selected document
$('form.htmleditorfield-linkform .ss-add.ss-upload').hide();
return this._super();
}
}
});
});
// Select the correct radio button
$('form.htmleditorfield-linkform input[name=LinkType][value=document]').click();
return returnArray;
} else {
// Clear the selected document
$('.document-add-existing').selectdocument();
$('form.htmleditorfield-linkform .ss-add.ss-upload').hide();
return this._super();
}
}
});
});
}(jQuery));

View File

@ -9,14 +9,34 @@ en:
TYPE: 'File type'
URL: URL
DMSDocument:
COVERIMAGE: Cover Image
DESCRIPTION: Description
EDIT: Edit
EDITDOCUMENT: Edit this document
PLURALNAME: Documents
RelatedPages: 'Related Pages'
RelatedReferences: 'Related References'
SINGULARNAME: Document
Versions: Versions
DMSTag:
PLURALNAME: 'D M S Tags'
SINGULARNAME: 'D M S Tag'
DOWNLOAD: "Download {title}"
LASTCHANGED: "Last changed: {date}"
TITLE: Title
DMSDocumentSet:
ADDDOCUMENTBUTTON: Add Document
ADDDOCUMENTSBUTTON: Add Documents
DIRECTION_ASCENDING: Ascending
DIRECTION_DESCENDING: Descending
GRIDFIELD_NOTICE: Managing documents will be available once you have created this document set.
PLURALNAME: Document Sets
QUERY_BUILDER_NOTICE: The query builder provides the ability to add documents to a document set based on the filters below. Please note that the set will be built using this criteria when you save the form. This set will not be dynamically updated (see the documentation for more information).
SHOWONPAGE: Show on page
SINGULARNAME: Document Set
TAGS_RIGHT_TITLE: Tags can be set in the taxonomy area, and can be assigned when editing a document.
DMSSiteTreeExtension:
DocumentSetsTabTitle: 'Document Sets ({count})'
DMSDocumentTaxonomyExtension:
TAGS: Tags
NOTAGS: No tags found
FileIFrameField:
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
GridAction:
@ -27,14 +47,25 @@ en:
DeletePermissionsFailure: 'No delete permissions'
UploadField:
ATTACHFILE: 'Attach a file'
DONE: Done!
DROPFILE: 'drop a file'
FIELDNOTSET: 'File information not found'
FROMCMS: From the CMS
FROMCOMPUTER: 'From your computer'
FROMCOMPUTERINFO: 'Upload from your computer'
MAXNUMBEROFFILES: 'Max number of {count} file(s) exceeded.'
DMSDocumentAddController:
MENUTITLE: 'Edit Page'
NODOCUMENTS: 'There are no documents attached to the selected page.'
RELATEDDOCUMENTS: 'Related Documents'
MAINTAB: Main
DMSDocument_versions:
PLURALNAME: 'D M S Document_versionss'
SINGULARNAME: 'D M S Document_versions'
PLURALNAME: 'DMS Document_versionss'
SINGULARNAME: 'DMS Document_versions'
DMSDocumentAddExistingField:
ADDEXISTING: Add Existing
AUTOCOMPLETE: Search by ID or filename
CHOOSESET: Choose Document Set
EDITDOCUMENTDETAILS: Edit Document Details
LINKADOCUMENT: Link a Document
SELECTED: Selected Document

View File

@ -13,9 +13,6 @@ es:
RelatedPages: 'Páginas relacionadas'
RelatedReferences: 'Referencias relacionadas'
SINGULARNAME: Documento
DMSTag:
PLURALNAME: 'Etiquetas del Sistema documental'
SINGULARNAME: 'Etiqueta del Sistema documental'
FileIFrameField:
ATTACHONCESAVED2: 'Archivos pueden ser adjuntados una vez que guardes por primera vez.'
GridAction:

View File

@ -13,9 +13,6 @@ nl:
RelatedPages: 'Gerelateerde paginas'
RelatedReferences: 'Gerelateerde referenties'
SINGULARNAME: 'Document'
DMSTag:
PLURALNAME: 'D M S Tags'
SINGULARNAME: 'D M S Tag'
FileIFrameField:
ATTACHONCESAVED2: 'Bestanden kunnen worden toegevoegd na het voor het eerst opslaan'
GridAction:

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "silverstripe-dms",
"version": "2.0.0",
"description": "SilverStripe Document Management System",
"directories": {
"doc": "docs",
"test": "tests"
},
"devDependencies": {
"css-loader": "^0.28.1",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.11.1",
"node-sass": "^4.13.1",
"sass-loader": "^6.0.3",
"style-loader": "^0.17.0",
"webpack": "^2.5.1"
},
"scripts": {
"build": "webpack --bail --progress",
"watch": "webpack --watch --progress"
},
"repository": {
"type": "git",
"url": "git://github.com/silverstripe/silverstripe-dms.git"
},
"keywords": [
"silverstripe",
"dms"
],
"author": "SilverStripe Ltd",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/silverstripe/silverstripe-dms/issues"
},
"homepage": "https://github.com/silverstripe/silverstripe-dms#readme"
}

View File

@ -1,218 +0,0 @@
@import "compass/css3";
@import '_mixins';
$color-button-generic: #e6e6e6 !default;
form.small .field input.text,
form.small .field textarea,
form.small .field select,
form.small .field .TreeDropdownField,
.field.small input.text, .field.small textarea,
.field.small select, .field.small .TreeDropdownField{
width: 100%;
}
#FileP{
.fieldgroup-field{
width:100%;
.cms-file-info-preview{
box-shadow:none;
}
.cms-file-info-data{
width:400px;
}
.fieldholder-small{
margin-top: 5px;
label{
width:auto;
margin-left: 0;
padding-top: 0;
margin-right: 10px;
font-weight: bold;
float: left;
}
.readonly{
font-style:italic;
color: #666;
}
}
}
}
#DocumentTypeID{
@include tabButtons;
}
#Actions{
box-shadow:none;
border:none;
padding:0;
li{
margin-left: 2px;
&:first-child{
margin-left: 0;
}
&.delete-button-appended{
display: inline-block;
position: relative;
margin-left: 0;
}
&.dms-active{
@include active-actions-btn;
}
}
}
.DMSDocumentActionsPanel{
/*.middleColumn{
margin-left:184px;
width: 510px;
form.small &{
margin-left: 120px;
}
}
*/
/*label.fieldholder-small-label{
display: none;
}*/
.fieldgroup {
display: none;
float:none;
width:auto;
background: #f8f8f8;
padding: 15px;
border: 1px solid #d0d3d5;
@include border-radius(5px);
.fieldholder-small{
label{
float:none;
width:auto;
margin:0;
padding:0;
&.ss-ui-button{
float: left;
margin: 0 10px 0 0;
}
}
}
.embargo, .expiry{
li{
float: none;
width: 100%;
margin-left: 8px;
label{
padding-left: 10px;
}
}
.embargoDatetime, .expiryDatetime{
margin-top: 0;
margin-left: 34px;
overflow: hidden;
.field{
&.date, &.time{
display: inline-block;
width: auto;
margin: 0;
padding: 0;
}
&.date{
.middleColumn{
background: url('../images/calendar-month.png') 90px 7px no-repeat;
overflow: hidden;
}
}
&.time{
.middleColumn{
background: url('../images/clock-frame.png') 90px 7px no-repeat;
overflow: hidden;
}
}
}
.middleColumn{
margin-left: 0;
width: auto;
border: none;
input{
width: 80px;
margin-right: 40px;
}
}
}
}
}
#Embargo {
border: none;
box-shadow: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
}
.ss-uploadfield-files{
.ss-uploadfield-item-preview{
background: url('../images/app_icons/generic_32.png') -10px -6px no-repeat;
}
.ss-uploadfield-item-name{
span.name{
width: 260px;
}
}
.ss-uploadfield-item-actions{
.ss-uploadfield-item-cancel{
width: auto;
text-indent:0;
.btn-icon-deleteLight{
background-position: 0 -128px;
display: inline-block;
}
.ui-button-text{
display: block;
position: relative;
float: right;
color: #555;
padding:0;
padding-left: 2em;
}
}
}
}
& > .fieldgroup.middleColumn {
overflow: hidden;
display: block;
.fieldgroup-field {
width: 100%;
}
}
}
#Form_ItemEditForm{
fieldset{
table.ss-gridfield-table{
width: 494px;
//max-width:508px;
tr{
th.main{
min-width:175px;
&.col-action_SetOrderID{
width:60px;
min-width:60px;
}
}
td{
white-space:normal;
}
}
}
}
}
#ui-datepicker-div{
border: 1px solid #DDD;
}

View File

@ -1,349 +0,0 @@
@import '_mixins';
//data-icon="cross-circle"
$gf_colour_zebra: #F0F4F7;
.ui-autocomplete{
border: 1px solid #DDD;
box-shadow: 0 1px 2px 0px #AFAFAF;
max-height: 300px;
overflow: scroll;
}
.cms fieldset.documents, .cms fieldset.relatedLinks{
table{
td{
white-space:normal;
}
td.col-buttons{
white-space:nowrap;
.dms-delete-link-only{
.ui-icon{
background: url(../images/chain-unchain.png) no-repeat;
}
}
}
tr.dms-document-hidden-row {
&.odd {
background-color: mix($gf_colour_zebra, rgba(255, 4, 0, 0.2));
}
&.even {
background-color: mix($gf_colour_zebra, rgba(255, 4, 0, 0.35));
}
&:hover {
background-color: mix($gf_colour_zebra, rgba(255, 4, 0, 0.6)) !important;
}
}
}
}
.DMSDocumentAddController .ui-tabs{
ul.ui-tabs-nav{
border-bottom:none;
float: right;
margin: 8px 0 -1px 0;
padding: 0 24px 0 0;
li{
padding-bottom: 1px;
border: 1px solid #C0C0C2;
a{
padding: 8px 20px 8px;
}
}
}
}
.ss-add {
.document-add-existing {
input.document-autocomplete {
position: absolute;
z-index: 9999;
//top: 70px;
width: 390px;
padding: 9px 7px;
border-bottom-right-radius:0;
border-top-right-radius:0;
outline:none;
box-sizing:border-box;
-moz-box-sizing: border-box;
&[disabled] {
color: #C0C0C2;
text-shadow: 0 -1px 0 #FFF;
background: #EEE;
background-image:none;
box-shadow: inset 0 1px 8px 0 #C4C4C4;
border-bottom-left-radius:0;
border-bottom-right-radius:0;
}
}
.TreeDropdownField {
border:none;
width: 100%;
max-width: 512px;
box-sizing: border-box;
cursor:pointer;
}
.treedropdownfield-toggle-panel-link {
padding: 5px 9px 9px;
background: #fff;
border: 1px solid #B3B3B3;
float: right;
z-index: 99999;
position: relative;
&.treedropdownfield-open-tree {
background: #fff;
border: 1px solid #B3B3B3;
border-bottom:none;
border-bottom-right-radius:0;
}
}
.treedropdownfield-title {
width: auto;
}
.treedropdownfield-toggle-panel-link a {
display: inline-block;
top: 4px;
position: relative;
}
.document-list {
width: 510px;
border: 1px solid #DDD;
border-top:none;
background: #ffffff;
display: none;
box-shadow:0 2px 4px 1px #DDD;
max-height:300px;
border-radius: 6px;
background-clip: padding-box;
overflow:scroll;
p {
padding: 10px 10px 0;
}
ul {
padding: 4px 0;
li {
line-height:18px;
a {
display: block;
padding: 4px 8px;
border: 1px solid #FFF;
color: black;
&:hover {
border: 1px solid #CCC;
border-radius: 4px;
background: rgba(203, 203, 203, 0.4);
cursor: pointer;
text-decoration:none;
outline:none;
text-shadow:none;
}
}
}
}
}
&.link-editor-context{
label{
float: left;
display: block;
width: 176px;
padding:8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white;
}
.middleColumn{
margin-left: 184px;
input{
background:white;
border: 1px solid #B3B3B3;
line-height: 16px;
margin: 0;
border-radius: 4px;
background-size: 100%;
max-width: 512px;
}
}
}
}
.ss-assetuploadfield{
&.link-editor-context{
label{
float: left;
display: block;
width: 176px;
padding:8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white;
}
.middleColumn{
margin-left: 184px;
display: block;
padding: 8px 8px 8px 0;
font-style: italic;
min-height:20px;
}
}
.step4{
margin-bottom: 10px;
}
}
}
//Styling for tree dropdown field
.cms{
.ss-add, .selectiongroup{
.treedropdownfield-panel {
margin: -1px 0 0 0;
box-sizing: border-box;
ul {
padding: 4px 0;
li {
border: 1px solid #ffffff;
a {
display: block;
padding: 4px 2px;
&.jstree-hovered {
background: rgba(203, 203, 203, 0.4);
border: 1px solid #CCC;
}
}
}
}
}
}
}
// Hack for Firefox to fix padding on adding document input.
// FF renders the size different and pushes it out by 2px compared to webkit
@-moz-document url-prefix() {
.ss-add{
.document-add-existing{
input{
padding: 10px 7px;
}
}
}
}
#Form_EditForm_Documents{
padding: 1em 0;
input[name="filter[LastChanged]"] {
display: none;
}
}
#Form_EditForm_RelatedLinks{
table{
padding: 1em 0;
thead{
h2{
display: none;
}
}
}
}
#SectionID, #DocumentTypeID{
@include tabButtons;
}
#Form_ItemEditForm{
h3:first-child{
display: inline-block;
float: left;
width: 184px;
}
ul.SelectionGroup{
display: inline-block;
position: relative;
padding: 0;
margin-top: 9px;
margin-left: 0;
height: 110px;
background: none;
border: none;
li{
width: auto;
clear: none;
display: inline;
margin-right: 4px;
input{
&.selector{
display:none;
}
}
label{
&.ui-button{
@include background(
linear-gradient(color-stops(
lighten($color-button-generic, 10%),
darken($color-button-generic, 5%)
))
);
font-weight: bold;
border: 1px solid #C0C0C2;
border-radius: 3px;
padding: 0.8em 1.5em;
&:hover {
box-shadow: 0 0 5px rgba(0,0,0,0.3);
}
}
}
div.field{
margin-left: 0px;
margin-bottom: 1em;
width: 600px;
position: absolute;
left:0;
margin-top: 13px;
padding: 10px;
background: white;
border: 1px solid #B3B3B3;
@include border-radius(4px);
@include background-image(linear-gradient(#efefef, #fff 10%, #fff 90%, #efefef));
}
&.selected{
label.ui-button{
@include active-actions-btn;
&:after {
top: 43px;
}
}
}
}
.treedropdownfield-panel{
margin: 1px 0 0 -1px;
box-sizing: content-box;
ul{
li{
display: block;
clear: both;
width: 100%;
margin: 0;
li {
padding-left: 20px;
}
}
}
}
}
}

View File

@ -1,85 +1,7 @@
@import "compass/css3";
$color-button-generic: #e6e6e6 !default;
@mixin tabButtons{
.middleColumn{
overflow:auto;
min-width: 800px;
ul{
padding:0;
input[type="radio"]{
display:none;
}
li{
display:table;
padding: 0;
width:125px;
height:40px;
white-space:normal;
margin-right: -1px;
border-radius: 0;
&:first-child{
border-radius: 6px 0 0 6px;
border-left: 1px solid #C0C0C2;
}
&:last-child{
border-radius: 0 6px 6px 0;
}
&.selected, .ie8 &.selected{
border-bottom:1px solid #C0C0C2;
@include background(
linear-gradient(color-stops(
darken($color-button-generic, 15%),
$color-button-generic
))
);
background-color: darken($color-button-generic, 15%);
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */
box-shadow: 0 1px 1px 0 #A0A0A0 inset;
:after{
box-shadow: 0 1px 1px 0 #DDD;
}
}
label{
display: table-cell;
vertical-align: middle;
text-align: center;
padding: 0 10px;
}
}
}
}
//** Generate a state for an active list item link
//
// @param {string} $color - the colour to use for the border
// @param {string} $size - the size the border should be, e.g. "8px"
@mixin dmsdocument-actions-active($color, $size: "4px") {
border-bottom: #{$size} solid #{$color};
}
@mixin active-actions-btn{
border-bottom:1px solid #C0C0C2;
@include background(
linear-gradient(color-stops(
darken($color-button-generic, 15%),
$color-button-generic
))
);
background-color: darken($color-button-generic, 15%);
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6'); /* IE6 & IE7 */
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#C0C0C2', endColorstr='#E6E6E6')"; /* IE8 */
box-shadow: 0 1px 1px 0 #A0A0A0 inset;
&:after{
content: '';
display: block;
position: absolute;
top: 33px;
left: 50%;
margin-left: -6px;
width: 12px;
height: 12px;
background: #F8F8F8;
border-right: 1px solid #B3B3B3;
border-top: 1px solid #B3B3B3;
-moz-transform:rotate(-45deg);
-webkit-transform:rotate(-45deg);
pointer-events:none;
z-index: 10;
}
}

12
scss/_variables.scss Normal file
View File

@ -0,0 +1,12 @@
//** Default values for colours, sizes etc
// Colours
$color-button-generic: #e6e6e6 !default;
$color-zebra: #F0F4F7 !default;
$color-grey-lighter: #f8f8f8 !default;
$color-grey-light: #d0d3d5 !default;
$color-grey-dark: #66727d !default;
$color-white: #ffffff !default;
$color-cms-input-border: #b3b3b3 !default;

203
scss/cmsfields.scss Normal file
View File

@ -0,0 +1,203 @@
//** The DMS document details/information on an edit page
.dmsdocument-documentdetails {
.fieldgroup-field {
.cms-file-info-preview {
box-shadow: none;
}
.cms-file-info-data {
width: 400px;
}
.fieldholder-small {
margin-top: 5px;
.fieldholder-small-label {
font-weight: bold;
float: left;
}
.readonly {
font-style: italic;
}
}
}
}
//** The actions panel buttons when editing a DMS document
.dmsdocment-actions {
border: none;
box-shadow: none;
margin-bottom: 0;
padding: 0;
label.left {
padding: 6px 0 0 0;
}
.ss-ui-button {
border: none;
background: none;
border-radius: 0;
font-weight: normal;
.ui-button-text {
padding-bottom: 1em;
}
&.dms-active,
&.dms-active:hover {
@include dmsdocument-actions-active($color-grey-dark);
}
&:hover,
&:active {
background: none;
border: none;
box-shadow: none;
}
}
}
//** The actions panel, containing mini forms for each DMS document action
.dmsdocument-actionspanel {
margin-top: 0;
.fieldgroup {
border: 1px solid $color-grey-light;
display: none;
margin-left: 0;
padding: 5px 15px;
.fieldholder-small {
label {
padding: 0;
}
}
.embargo,
.expiry {
li {
margin-left: 8px;
width: 100%;
label {
padding-left: 10px;
}
}
.embargoDatetime,
.expiryDatetime {
float: left;
margin-top: 0;
margin-left: 34px;
overflow: hidden;
.field {
&.date,
&.time {
display: inline-block;
margin: 0;
padding: 0;
width: auto;
.middleColumn {
overflow: hidden;
}
}
&.date {
.middleColumn {
background: url('../images/calendar-month.png') 90px 7px no-repeat;
}
}
&.time {
.middleColumn {
background: url('../images/clock-frame.png') 90px 7px no-repeat;
}
}
}
.middleColumn {
border: none;
margin-left: 0;
width: auto;
input.date,
input.time {
margin-right: 40px;
width: 80px;
}
}
}
}
.fieldgroup-field label {
margin: 0;
}
}
.ss-uploadfield-files {
.ss-uploadfield-item-preview {
background: url('../images/app_icons/generic_32.png') -10px -6px no-repeat;
}
.ss-uploadfield-item-name {
span.name {
width: 260px;
}
}
.ss-uploadfield-item-actions {
.ss-uploadfield-item-cancel {
text-indent: 0;
width: auto;
.btn-icon-deleteLight {
background-position: 0 -128px;
display: inline-block;
}
.ui-button-text {
color: $color-grey-dark;
display: block;
float: right;
padding: 0 0 0 2em;
position: relative;
}
}
}
}
& > .fieldgroup.middleColumn {
display: block;
overflow: hidden;
.fieldgroup-field {
width: 100%;
}
}
.permissions {
.fieldholder-small {
clear: both;
}
}
}
#ui-datepicker-div {
border: 1px solid $color-grey-light;
}
//** Overrides for the inline edit screen
form.small .field input.text,
form.small .field textarea,
form.small .field select,
form.small .field .TreeDropdownField,
.field.small input.text,
.field.small textarea,
.field.small select,
.field.small .TreeDropdownField {
width: 100%;
}

4
scss/main.scss Normal file
View File

@ -0,0 +1,4 @@
@import "_variables";
@import "_mixins";
@import "cmsfields";
@import "upload";

273
scss/upload.scss Normal file
View File

@ -0,0 +1,273 @@
.dmsdocument-addexisting {
.ui-autocomplete {
border: 1px solid $color-grey-light;
max-height: 300px;
overflow: scroll;
}
}
.ss-add {
.document-add-existing {
input.document-autocomplete {
position: absolute;
z-index: 9999;
width: 390px;
padding: 9px 7px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
outline: none;
box-sizing: border-box;
-moz-box-sizing: border-box;
&[disabled] {
color: $color-grey-light;
text-shadow: 0 -1px 0 $color-white;
background: $color-grey-lighter;
background-image:none;
box-shadow: inset 0 1px 8px 0 $color-grey-light;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.treedropdown {
border: none;
}
.treedropdownfield-toggle-panel-link {
padding: 5px 9px 9px;
background: #fff;
border: 1px solid $color-cms-input-border;
float: right;
z-index: 99999;
position: relative;
&.treedropdownfield-open-tree {
background: #fff;
border: 1px solid $color-cms-input-border;
border-bottom:none;
border-bottom-right-radius:0;
}
}
.treedropdownfield-title {
width: auto;
}
.treedropdownfield-toggle-panel-link a {
display: inline-block;
top: 4px;
position: relative;
}
.document-list {
width: 510px;
border: 1px solid $color-grey-light;
border-top: none;
background: $color-white;
display: none;
max-height: 300px;
background-clip: padding-box;
overflow: scroll;
p {
padding: 10px 10px 0;
}
ul {
padding: 4px 0;
li {
line-height: 18px;
a {
display: block;
padding: 4px 8px;
border: 1px solid $color-white;
color: black;
&:hover {
border: 1px solid $color-grey-light;
border-radius: 4px;
background: rgba(203, 203, 203, 0.4);
cursor: pointer;
text-decoration: none;
outline: none;
text-shadow: none;
}
}
}
}
}
// When inserting a link to a document via the TinyMCE editor
&.link-editor-context {
label {
float: left;
display: block;
width: 176px;
padding: 8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white;
}
.middleColumn {
input {
background: white;
border: 1px solid $color-cms-input-border;
line-height: 16px;
border-radius: 4px;
background-size: 100%;
max-width: 468px;
&.document-autocomplete {
max-width: 365px;
}
}
}
.document-list {
width: calc(100% - 2px);
}
}
}
.ss-assetuploadfield {
&.link-editor-context {
label {
float: left;
display: block;
width: 176px;
padding: 8px 8px 8px 0;
line-height: 16px;
font-weight: bold;
text-shadow: 1px 1px 0 white;
}
.middleColumn {
margin-left: 184px;
display: block;
padding: 8px 8px 8px 0;
font-style: italic;
min-height: 20px;
}
}
.step4 {
margin-bottom: 10px;
}
}
}
// Styling for tree dropdown field
.cms {
.ss-add,
.selectiongroup {
.treedropdownfield-panel {
margin: -1px 0 0 0;
box-sizing: border-box;
ul {
padding: 4px 0;
li {
border: 1px solid $color-white;
a {
display: block;
padding: 4px 2px;
&.jstree-hovered {
background: rgba(203, 203, 203, 0.4);
border: 1px solid $color-grey-light;
}
}
}
}
}
}
}
#Form_ItemEditForm {
h3:first-child {
display: inline-block;
float: left;
width: 184px;
}
ul.SelectionGroup {
display: inline-block;
position: relative;
padding: 0;
margin-top: 9px;
margin-left: 0;
height: 110px;
background: none;
border: none;
li {
width: auto;
clear: none;
display: inline;
margin-right: 4px;
input {
&.selector {
display: none;
}
}
label {
&.ui-button {
font-weight: bold;
border: 1px solid #C0C0C2;
border-radius: 3px;
padding: 0.8em 1.5em;
&:hover {
box-shadow: 0 0 5px rgba(0,0,0,0.3);
}
}
}
div.field {
margin-left: 0px;
margin-bottom: 1em;
width: 600px;
position: absolute;
left: 0;
margin-top: 13px;
padding: 10px;
background: white;
border: 1px solid $color-cms-input-border;
}
&.selected {
label.ui-button {
&:after {
top: 43px;
}
}
}
}
.treedropdownfield-panel {
margin: 1px 0 0 -1px;
box-sizing: content-box;
ul {
li {
display: block;
clear: both;
width: 100%;
margin: 0;
li {
padding-left: 20px;
}
}
}
}
}
}

View File

@ -1,64 +1,43 @@
<div id="document" class="ss-add field treedropdown searchable">
<div class="document-add-existing <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<% if useFieldContext %>
<h3>
<% else %>
<div>
<% end_if %>
<span class="step-label">
<% if useFieldContext %>
<span class="flyout">1</span><span class="arrow"></span>
<strong class="title">Link a Document</strong>
<% else %>
<% end_if %>
</span>
<% if useFieldContext %>
</h3>
<% else %>
</div>
<% end_if %>
<div class="ss-add field treedropdown searchable dmsdocument-addexisting">
<div class="document-add-existing <% if $useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<% if $useFieldContext %><h3><% else %><div><% end_if %>
<span class="step-label">
<% if $useFieldContext %>
<span class="flyout">1</span><span class="arrow"></span>
<strong class="title"><%t DMSDocumentAddExistingField.LINKADOCUMENT "Link a Document" %></strong>
<% end_if %>
</span>
<% if $useFieldContext %></h3><% else %></div><% end_if %>
<% if useFieldContext %>
<% else %>
<label>Link a Document</label>
<div class="middleColumn">
<% end_if %>
<input class="document-autocomplete text" type="text" placeholder="Search by ID or filename" />
<!-- <span>or Add from page</span> -->
$fieldByName(PageSelector)
<% if not $useFieldContext %>
<label><%t DMSDocumentAddExistingField.LINKADOCUMENT "Link a Document" %></label>
<div class="middleColumn">
<% end_if %>
<input class="document-autocomplete text" type="text" placeholder="<%t DMSDocumentAddExistingField.AUTOCOMPLETE "Search by ID or filename" %>" />
$fieldByName(PageSelector)
<div class="document-list"></div>
<% if not $useFieldContext %>
</div>
<% end_if %>
</div>
<div class="document-list"></div>
<% if useFieldContext %>
<% else %>
</div>
<% end_if %>
</div>
<div class="ss-assetuploadfield <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<div class="step4">
<span class="step-label">
<% if useFieldContext %>
<span class="flyout">2</span><span class="arrow"></span>
<strong class="title">Edit Document Details</strong>
<% else %>
<label>Selected Document</label>
<% end_if %>
</span>
</div>
<!-- <div class="fileOverview"></div> -->
<% if useFieldContext %>
<% else %>
<div class="middleColumn">
<% end_if %>
<ul class="files ss-uploadfield-files ss-add-files"></ul>
<% if useFieldContext %>
<% else %>
</div>
<% end_if %>
</div>
</div>
<div class="ss-assetuploadfield <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<div class="step4">
<span class="step-label">
<% if useFieldContext %>
<span class="flyout">2</span><span class="arrow"></span>
<strong class="title"><%t DMSDocumentAddExistingField.EDITDOCUMENTDETAILS "Edit Document Details" %></strong>
<% else %>
<label><%t DMSDocumentAddExistingField.SELECTED "Selected Document" %></label>
<% end_if %>
</span>
</div>
<% if not $useFieldContext %>
<div class="middleColumn">
<% end_if %>
<ul class="files ss-uploadfield-files ss-add-files"></ul>
<% if not $useFieldContext %>
</div>
<% end_if %>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div class="clear"><!-- --></div>
</label>
<div class="ss-uploadfield-item-actions">
<% if Top.isDisabled || Top.isReadonly %>
<% if $Top.isDisabled || $Top.isReadonly %>
<% else %>
$UploadFieldFileButtons
<% end_if %>
@ -25,11 +25,11 @@
<% end_loop %>
<% end_if %>
</ul>
<% if isDisabled || isReadonly %>
<% if isSaveable %>
<% if $isDisabled || $isReadonly %>
<% if $isSaveable %>
<% else %>
<div class="ss-uploadfield-item">
<em><% _t('FileIFrameField.ATTACHONCESAVED2', 'Files can be attached once you have saved the record for the first time.') %></em>
<em><%t FileIFrameField.ATTACHONCESAVED2 "Files can be attached once you have saved the record for the first time." %></em>
</div>
<% end_if %>
<% else %>
@ -41,8 +41,8 @@
<label class="ss-uploadfield-item-name"><b>
<% _t('UploadField.ATTACHFILE', 'Attach a file') %>
</b></label>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<%t UploadField.FROMCOMPUTERINFO 'Upload from your computer' %>" data-icon="drive-upload">
<%t UploadField.FROMCOMPUTER 'From your computer' %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label>
<div class="clear"><!-- --></div>

View File

@ -0,0 +1,4 @@
<%-- Customised to change the button's link to the DMSDocumentAddController instead of the default --%>
<a href="$NewLink" class="action action-detail ss-ui-action-constructive ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link" data-icon="add">
$ButtonName
</a>

View File

@ -1,19 +1,20 @@
<% if isHidden != true %>
<div class="document $Extension">
<% if Title %>
<h4><a href="$Link" title="Download $Title">$Title</a></h4>
<% else %>
<h4><a href="$Link" title="Download $FilenameWithoutID">$FilenameWithoutID</a></h4>
<% end_if %>
<p class='details'>
<strong>$FilenameWithoutID</strong>
| $Extension
| $FileSizeFormatted
| Last Changed: $LastChanged.Nice
</p>
<% if Description %>
<p>$DescriptionWithLineBreak</p>
<% end_if %>
</div>
<% end_if %>
<% if not $isHidden %>
<div class="document $Extension">
<h4><a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$getTitle %>">$getTitle</a></h4>
<% if $CoverImage %>
<div class="article-thumbnail">
$CoverImage.FitMax(100, 100)
</div>
<% end_if %>
<p class="details"><% include DocumentDetails %></p>
<% if $Description %>
<p>$DescriptionWithLineBreak</p>
<% end_if %>
<% if $getRelatedDocuments %>
<% include RelatedDocuments %>
<% end_if %>
</div>
<% end_if %>

View File

@ -0,0 +1,4 @@
<strong>$FilenameWithoutID</strong>
| $Extension
| $FileSizeFormatted
| <%t DMSDocument.LASTCHANGED "Last changed: {date}" date=$LastEdited.Nice %>

View File

@ -0,0 +1,11 @@
<% if $getDocuments %>
<div class="documentsets-set">
<% if $Title %>
<h3>$Title</h3>
<% end_if %>
<% loop $getDocuments.Sort(DocumentSort) %>
<% include Document %>
<% end_loop %>
</div>
<% end_if %>

View File

@ -0,0 +1,7 @@
<% if $getDocumentSets %>
<div class="documentsets">
<% loop $getDocumentSets %>
<% include DocumentSet %>
<% end_loop %>
</div>
<% end_if %>

View File

@ -1,4 +0,0 @@
<% if PageDocuments %>
<div><h3>Documents:</h3></div>
<% loop PageDocuments %><% include Document %><% end_loop %>
<% end_if %>

View File

@ -0,0 +1,14 @@
<h5><%t DMSDocument.RELATED_DOCUMENTS "Related documents" %></h5>
<ul class="documents-relateddocuments">
<% loop $getRelatedDocuments %>
<li>
<% if $Title %>
<a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$Title %>">$Title</a>
<% else %>
<a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$FilenameWithoutID %>">$FilenameWithoutID</a>
<% end_if %>
<span class="documents-relateddocuments-documentdetails"><% include DocumentDetails %></span>
</li>
<% end_loop %>
</ul>

View File

@ -3,52 +3,69 @@
/**
* Class DMSDocumentControllerTest
*/
class DMSDocumentControllerTest extends SapphireTest {
class DMSDocumentControllerTest extends SapphireTest
{
protected static $fixture_file = 'dmstest.yml';
protected static $fixture_file = "dmstest.yml";
/**
* @var DMSDocument_Controller
*/
protected $controller;
public function testDownloadBehaviourOpen() {
DMS::$dmsFolder = DMS_DIR; //sneakily setting the DMS folder to the folder where the test file lives
public function setUp()
{
parent::setUp();
Config::inst()->update('DMS', 'folder_name', 'assets/_unit-test-123');
$this->logInWithPermission('ADMIN');
/** @var DMSDocument_Controller $controller */
$controller = $this->getMockBuilder('DMSDocument_Controller')
->setMethods(array('sendFile'))->getMock();
$self = $this;
$controller->expects($this->once())->method('sendFile')->will($this->returnCallback(function($path, $mime, $name, $disposition) use($self) {
$self->assertEquals('inline', $disposition);
}));
$openDoc = new DMSDocument();
$openDoc->Filename = "DMS-test-lorum-file.pdf";
$openDoc->Folder = "tests";
$openDoc->DownloadBehavior = 'open';
$openDoc->clearEmbargo(false);
$openDoc->write();
$request = new SS_HTTPRequest('GET', 'index/' . $openDoc->ID);
$request->match('index/$ID');
$controller->index($request);
$this->controller = $this->getMockBuilder('DMSDocument_Controller')
->setMethods(array('sendFile'))
->getMock();
}
public function testDownloadBehaviourDownload() {
DMS::$dmsFolder = DMS_DIR; //sneakily setting the DMS folder to the folder where the test file lives
$this->logInWithPermission('ADMIN');
/** @var DMSDocument_Controller $controller */
$controller = $this->getMockBuilder('DMSDocument_Controller')
->setMethods(array('sendFile'))->getMock();
$self = $this;
$controller->expects($this->once())->method('sendFile')->will($this->returnCallback(function($path, $mime, $name, $disposition) use($self) {
$self->assertEquals('attachment', $disposition);
}));
$openDoc = new DMSDocument();
$openDoc->Filename = "DMS-test-lorum-file.pdf";
$openDoc->Folder = "tests";
$openDoc->DownloadBehavior = 'download';
$openDoc->clearEmbargo(false);
$openDoc->write();
$request = new SS_HTTPRequest('GET', 'index/' . $openDoc->ID);
$request->match('index/$ID');
$controller->index($request);
public function tearDown()
{
DMSFilesystemTestHelper::delete('assets/_unit-test-123');
parent::tearDown();
}
/**
* Test that the download behaviour is either "open" or "download"
*
* @param string $behaviour
* @param string $expectedDisposition
* @dataProvider behaviourProvider
*/
public function testDownloadBehaviourOpen($behaviour, $expectedDisposition)
{
$self = $this;
$this->controller->expects($this->once())
->method('sendFile')
->will(
$this->returnCallback(function ($path, $mime, $name, $disposition) use ($self, $expectedDisposition) {
$self->assertEquals($expectedDisposition, $disposition);
})
);
$openDoc = DMS::inst()->storeDocument('dms/tests/DMS-test-lorum-file.pdf');
$openDoc->DownloadBehavior = $behaviour;
$openDoc->clearEmbargo(false);
$openDoc->write();
$request = new SS_HTTPRequest('GET', $openDoc->Link());
$request->match('dmsdocument/$ID');
$this->controller->index($request);
}
/**
* @return array[]
*/
public function behaviourProvider()
{
return array(
array('open', 'inline'),
array('download', 'attachment')
);
}
}

View File

@ -0,0 +1,270 @@
<?php
class DMSDocumentSetTest extends SapphireTest
{
protected static $fixture_file = 'dmstest.yml';
/**
* Ensure that getDocuments is extensible
*/
public function testGetDocumentsIsExtensible()
{
DMSDocumentSet::add_extension('StubRelatedDocumentExtension');
$set = new DMSDocumentSet;
$documents = $set->getDocuments();
$this->assertCount(1, $documents);
$this->assertSame('Extended', $documents->first()->Filename);
}
/**
* Test that the GridField for documents isn't shown until you've saved the set
*/
public function testGridFieldShowsWhenSetIsSaved()
{
$set = DMSDocumentSet::create();
// Not in database yet
$fields = $set->getCMSFields();
$this->assertNull($fields->fieldByName('Root.Main.Documents'));
$gridFieldNotice = $fields->fieldByName('Root.Main.GridFieldNotice');
$this->assertNotNull($gridFieldNotice);
$this->assertContains('Managing documents will be available', $gridFieldNotice->getContent());
// In the database
$set->Title = 'Testing';
$set->write();
$fields = $set->getCMSFields();
$this->assertNotNull($fields->fieldByName('Root.Main.Documents'));
$this->assertNull($fields->fieldByName('Root.Main.GridFieldNotice'));
}
public function testRelations()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s4 = $this->objFromFixture('SiteTree', 's4');
$ds1 = $this->objFromFixture('DMSDocumentSet', 'ds1');
$ds2 = $this->objFromFixture('DMSDocumentSet', 'ds2');
$ds3 = $this->objFromFixture('DMSDocumentSet', 'ds3');
$this->assertCount(0, $s4->getDocumentSets(), 'Page 4 has no document sets associated');
$this->assertCount(2, $s1->getDocumentSets(), 'Page 1 has 2 document sets');
$this->assertEquals(array($ds1->ID, $ds2->ID), $s1->getDocumentSets()->column('ID'));
}
/**
* Test that various components exist in the GridField config. See {@link DMSDocumentSet::getCMSFields} for context.
*/
public function testDocumentGridFieldConfig()
{
$set = $this->objFromFixture('DMSDocumentSet', 'ds1');
$fields = $set->getCMSFields();
$gridField = $fields->fieldByName('Root.Main.Documents');
$this->assertTrue((bool) $gridField->hasClass('documents'));
/** @var GridFieldConfig $config */
$config = $gridField->getConfig();
$this->assertNotNull($addNew = $config->getComponentByType('DMSGridFieldAddNewButton'));
$this->assertSame($set->ID, $addNew->getDocumentSetId());
if (class_exists('GridFieldPaginatorWithShowAll')) {
$this->assertNotNull($config->getComponentByType('GridFieldPaginatorWithShowAll'));
} else {
$paginator = $config->getComponentByType('GridFieldPaginator');
$this->assertNotNull($paginator);
$this->assertSame(15, $paginator->getItemsPerPage());
}
$sortableAssertion = class_exists('GridFieldSortableRows') ? 'assertNotNull' : 'assertNull';
$this->$sortableAssertion($config->getComponentByType('GridFieldSortableRows'));
}
/**
* Ensure that the display fields for the documents GridField can be returned
*/
public function testGetDocumentDisplayFields()
{
$document = $this->objFromFixture('DMSDocumentSet', 'ds1');
$this->assertInternalType('array', $document->getDocumentDisplayFields());
Config::inst()->update('DMSDocument', 'display_fields', array('apple' => 'Apple', 'orange' => 'Orange'));
$displayFields = $document->getDocumentDisplayFields();
$this->assertContains('Apple', $displayFields);
$this->assertContains('Orange', $displayFields);
$this->assertArrayHasKey('ManuallyAdded', $displayFields);
$this->assertContains('Added', $displayFields);
}
/**
* Tests to ensure that the callback for formatting ManuallyAdded will return a nice label for the user
*/
public function testNiceFormattingForManuallyAddedInGridField()
{
$fieldFormatting = $this->objFromFixture('DMSDocumentSet', 'ds1')
->getCMSFields()
->fieldByName('Root.Main.Documents')
->getConfig()
->getComponentByType('GridFieldDataColumns')
->getFieldFormatting();
$this->assertArrayHasKey('ManuallyAdded', $fieldFormatting);
$this->assertTrue(is_callable($fieldFormatting['ManuallyAdded']));
$this->assertSame('Manually', $fieldFormatting['ManuallyAdded'](1));
$this->assertSame('Query Builder', $fieldFormatting['ManuallyAdded'](0));
}
/**
* Test that query fields can be added to the gridfield
*/
public function testAddQueryFields()
{
/** @var DMSDocumentSet $set */
$set = $this->objFromFixture('DMSDocumentSet', 'ds6');
/** @var FieldList $fields */
$fields = new FieldList(new TabSet('Root'));
/** @var FieldList $fields */
$set->addQueryFields($fields);
$keyValuePairs = $fields->dataFieldByName('KeyValuePairs');
$this->assertNotNull(
$keyValuePairs,
'addQueryFields() includes KeyValuePairs composite field'
);
$this->assertNotNull(
$keyValuePairs->fieldByName('KeyValuePairs[Title]'),
'addQueryFields() includes KeyValuePairs composite field'
);
// Test that the notification field exists
$this->assertNotNull($fields->fieldByName('Root.QueryBuilder.GridFieldNotice'));
// Test that Tags__ID field exists
$this->assertContains(
'Tags can be set in the taxonomy area,',
$keyValuePairs->fieldByName('KeyValuePairs[Tags__ID]')->RightTitle()
);
}
/**
* Ensure that the "direction" dropdown field has user friendly field labels
*/
public function testQueryBuilderDirectionFieldHasFriendlyLabels()
{
$fields = $this->objFromFixture('DMSDocumentSet', 'ds1')->getCMSFields();
$dropdown = $fields->fieldByName('Root.QueryBuilder')->FieldList()->filterByCallback(function ($field) {
return $field instanceof FieldGroup;
})->first()->fieldByName('SortByDirection');
$this->assertInstanceOf('DropdownField', $dropdown);
$source = $dropdown->getSource();
$this->assertContains('Ascending', $source);
$this->assertContains('Descending', $source);
}
/**
* Ensure that the configurable shortcode handler key is a hidden field in the CMS
*/
public function testShortcodeHandlerKeyFieldExists()
{
Config::inst()->update('DMS', 'shortcode_handler_key', 'unit-test');
$set = DMSDocumentSet::create(array('Title' => 'TestSet'));
$set->write();
$fields = $set->getCMSFields();
$field = $fields->fieldByName('Root.Main.DMSShortcodeHandlerKey');
$this->assertInstanceOf('HiddenField', $field);
$this->assertSame('unit-test', $field->Value());
}
/**
* Ensure that if the module is available, the orderable rows GridField component is added
*/
public function testDocumentsAreOrderable()
{
if (!class_exists('GridFieldSortableRows')) {
$this->markTestSkipped('Test requires undefinedoffset/sortablegridfield installed.');
}
$fields = $this->objFromFixture('DMSDocumentSet', 'ds1')->getCMSFields();
$gridField = $fields->fieldByName('Root.Main.Documents');
$this->assertInstanceOf('GridField', $gridField);
$this->assertInstanceOf(
'GridFieldSortableRows',
$gridField->getConfig()->getComponentByType('GridFieldSortableRows')
);
}
/**
* Test that extra documents are added after write
*/
public function testSaveLinkedDocuments()
{
/** @var DMSDocumentSet $set */
$set = $this->objFromFixture('DMSDocumentSet', 'dsSaveLinkedDocuments');
// Assert initially docs
$this->assertEquals(1, $set->getDocuments()->count(), 'Set has 1 document');
// Now apply the query and see if 2 extras were added with CreatedByID filter
$set->KeyValuePairs = '{"Filename":"extradoc3"}';
$set->saveLinkedDocuments();
$this->assertEquals(2, $set->getDocuments()->count(), 'Set has 2 documents');
}
/**
* Tests that an exception is thrown if no title entered for a DMSDocumentSet.
* @expectedException ValidationException
*/
public function testExceptionOnNoTitleGiven()
{
DMSDocumentSet::create(array('Title' => ''))->write();
}
/**
* Ensure that when editing in a page context that the "page" field is removed, or is labelled "Show on page"
* otherwise
*/
public function testPageFieldRemovedWhenEditingInPageContext()
{
$set = $this->objFromFixture('DMSDocumentSet', 'ds1');
$fields = $set->getCMSFields();
$this->assertInstanceOf('DropdownField', $fields->fieldByName('Root.Main.PageID'));
$pageController = new CMSPageEditController;
$pageController->pushCurrent();
$fields = $set->getCMSFields();
$this->assertNull($fields->fieldByName('Root.Main.PageID'));
}
/**
* Tests all crud permissions
*/
public function testPermissions()
{
if ($member = Member::currentUser()) {
$member->logout();
}
$set = $this->objFromFixture('DMSDocumentSet', 'ds1');
$this->assertFalse($set->canCreate());
$this->assertFalse($set->canDelete());
$this->assertFalse($set->canEdit());
$this->assertFalse($set->canView());
$this->logInWithPermission('CMS_ACCESS_DMSDocumentAdmin');
$this->assertTrue($set->canCreate());
$this->assertTrue($set->canDelete());
$this->assertTrue($set->canEdit());
$this->assertTrue($set->canView());
}
}

View File

@ -1,164 +1,10 @@
<?php
class DMSDocumentTest extends SapphireTest
{
protected static $fixture_file = 'dmstest.yml';
protected static $fixture_file = "dms/tests/dmstest.yml";
public function tearDownOnce()
public function testDefaultDownloadBehabiourCMSFields()
{
self::$is_running_test = true;
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
self::$is_running_test = $this->originalIsRunningTest;
}
public function testPageRelations()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s3 = $this->objFromFixture('SiteTree', 's3');
$s4 = $this->objFromFixture('SiteTree', 's4');
$s5 = $this->objFromFixture('SiteTree', 's5');
$s6 = $this->objFromFixture('SiteTree', 's6');
$d1 = $this->objFromFixture('DMSDocument', 'd1');
$pages = $d1->Pages();
$pagesArray = $pages->toArray();
$this->assertEquals($pagesArray[0]->ID, $s1->ID, "Page 1 associated correctly");
$this->assertEquals($pagesArray[1]->ID, $s2->ID, "Page 2 associated correctly");
$this->assertEquals($pagesArray[2]->ID, $s3->ID, "Page 3 associated correctly");
$this->assertEquals($pagesArray[3]->ID, $s4->ID, "Page 4 associated correctly");
$this->assertEquals($pagesArray[4]->ID, $s5->ID, "Page 5 associated correctly");
$this->assertEquals($pagesArray[5]->ID, $s6->ID, "Page 6 associated correctly");
}
public function testAddPageRelation()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s3 = $this->objFromFixture('SiteTree', 's3');
$doc = new DMSDocument();
$doc->Filename = "test file";
$doc->Folder = "0";
$doc->write();
$doc->addPage($s1);
$doc->addPage($s2);
$doc->addPage($s3);
$pages = $doc->Pages();
$pagesArray = $pages->toArray();
$this->assertEquals($pagesArray[0]->ID, $s1->ID, "Page 1 associated correctly");
$this->assertEquals($pagesArray[1]->ID, $s2->ID, "Page 2 associated correctly");
$this->assertEquals($pagesArray[2]->ID, $s3->ID, "Page 3 associated correctly");
$doc->removePage($s1);
$pages = $doc->Pages();
$pagesArray = $pages->toArray(); //page 1 is missing
$this->assertEquals($pagesArray[0]->ID, $s2->ID, "Page 2 still associated correctly");
$this->assertEquals($pagesArray[1]->ID, $s3->ID, "Page 3 still associated correctly");
$documents = $s2->Documents();
$documentsArray = $documents->toArray();
$this->assertDOSContains(array(array('Filename'=>$doc->Filename)), $documentsArray, "Document associated with page");
$doc->removeAllPages();
$pages = $doc->Pages();
$this->assertEquals($pages->Count(), 0, "All pages removed");
$documents = $s2->Documents();
$documentsArray = $documents->toArray();
$this->assertNotContains($doc, $documentsArray, "Document no longer associated with page");
}
public function testDeletingPageWithAssociatedDocuments()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s2->publish('Stage', 'Live');
$s2ID = $s2->ID;
$doc = new DMSDocument();
$doc->Filename = "delete test file";
$doc->Folder = "0";
$doc->write();
$doc->addPage($s1);
$doc->addPage($s2);
$s1->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'", false);
$this->assertEquals(
$documents->Count(),
'1',
"Deleting one of the associated page doesn't affect the single document we created"
);
$s2->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'1',
"Deleting a page from draft stage doesn't delete the associated docs,"
. "even if it's the last page they're associated with"
);
$s2 = Versioned::get_one_by_stage('SiteTree', 'Live', sprintf('"SiteTree"."ID" = %d', $s2ID));
$s2->doDeleteFromLive();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'0',
"However, deleting the live version of the last page that a document is "
."associated with causes that document to be deleted as well"
);
}
public function testUnpublishPageWithAssociatedDocuments()
{
$s2 = $this->objFromFixture('SiteTree', 's2');
$s2->publish('Stage', 'Live');
$s2ID = $s2->ID;
$doc = new DMSDocument();
$doc->Filename = "delete test file";
$doc->Folder = "0";
$doc->write();
$doc->addPage($s2);
$s2->doDeleteFromLive();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'1',
"Deleting a page from live stage doesn't delete the associated docs,"
. "even if it's the last page they're associated with"
);
$s2 = Versioned::get_one_by_stage('SiteTree', 'Stage', sprintf('"SiteTree"."ID" = %d', $s2ID));
$s2->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'0',
"However, deleting the draft version of the last page that a document is "
."associated with causes that document to be deleted as well"
);
}
public function testDefaultDownloadBehabiourCMSFields() {
$document = singleton('DMSDocument');
Config::inst()->update('DMSDocument', 'default_download_behaviour', 'open');
$cmsFields = $document->getCMSFields();
@ -169,4 +15,341 @@ class DMSDocumentTest extends SapphireTest
$cmsFields = $document->getCMSFields();
$this->assertEquals('download', $cmsFields->dataFieldByName('DownloadBehavior')->Value());
}
/**
* Ensure that related documents can be retrieved for a given DMS document
*/
public function testRelatedDocuments()
{
$document = $this->objFromFixture('DMSDocument', 'document_with_relations');
$this->assertGreaterThan(0, $document->RelatedDocuments()->count());
$this->assertEquals(
array('test-file-file-doesnt-exist-1', 'test-file-file-doesnt-exist-2'),
$document->getRelatedDocuments()->column('Filename')
);
}
/**
* Test the extensibility of getRelatedDocuments
*/
public function testGetRelatedDocumentsIsExtensible()
{
DMSDocument::add_extension('StubRelatedDocumentExtension');
$emptyDocument = new DMSDocument;
$relatedDocuments = $emptyDocument->getRelatedDocuments();
$this->assertCount(1, $relatedDocuments);
$this->assertSame('Extended', $relatedDocuments->first()->Filename);
}
/**
* Ensure that the DMS Document CMS actions contains a grid field for managing related documents
*/
public function testDocumentHasCmsFieldForManagingRelatedDocuments()
{
$document = $this->objFromFixture('DMSDocument', 'document_with_relations');
$gridField = $this->getRelatedDocumentsGridField($document);
$this->assertInstanceOf('GridField', $gridField);
$gridFieldConfig = $gridField->getConfig();
$this->assertNotNull(
'GridFieldAddExistingAutocompleter',
$addExisting = $gridFieldConfig->getComponentByType('GridFieldAddExistingAutocompleter'),
'Related documents GridField has an "add existing" autocompleter'
);
$this->assertNull(
$gridFieldConfig->getComponentByType('GridFieldAddNewButton'),
'Related documents GridField does not have an "add new" button'
);
}
/**
* Ensures that the DMS Document CMS Related and Versions fields are removed if user can't edit
*/
public function testDocumentHasNoCMSFieldsForManagingRelatedDocumentsIfCantEdit()
{
$this->logInWithPermission('another-user');
$document = $this->objFromFixture('DMSDocument', 'doc-only-these-users');
$gridField = $this->getRelatedDocumentsGridField($document);
$this->assertNull($gridField);
}
/**
* Ensure that the related documents list does not include the current document itself
*/
public function testGetRelatedDocumentsForAutocompleter()
{
$document = $this->objFromFixture('DMSDocument', 'd1');
$gridField = $this->getRelatedDocumentsGridField($document);
$this->assertInstanceOf('GridField', $gridField);
$config = $gridField->getConfig();
$autocompleter = $config->getComponentByType('GridFieldAddExistingAutocompleter');
$autocompleter->setResultsFormat('$Filename');
$jsonResult = $autocompleter->doSearch(
$gridField,
new SS_HTTPRequest('GET', '/', array('gridfield_relationsearch' => 'test'))
);
$this->assertNotContains('test-file-file-doesnt-exist-1', $jsonResult);
$this->assertContains('test-file-file-doesnt-exist-2', $jsonResult);
$this->assertEquals(array('Title:PartialMatch', 'Filename:PartialMatch'), $autocompleter->getSearchFields());
}
/**
* @return GridField
*/
protected function getRelatedDocumentsGridField(DMSDocument $document)
{
$documentFields = $document->getCMSFields();
/** @var FieldGroup $actions */
$actions = $documentFields->fieldByName('ActionsPanel');
$gridField = null;
foreach ($actions->getChildren() as $child) {
/** @var FieldGroup $child */
if ($gridField = $child->fieldByName('RelatedDocuments')) {
break;
}
}
return $gridField;
}
/**
* Ensure that HTML is returned containing list items with action panel steps
*/
public function testGetActionTaskHtml()
{
$document = $this->objFromFixture('DMSDocument', 'd1');
$document->addActionPanelTask('example', 'Example');
$result = $document->getActionTaskHtml();
$this->assertContains('<label class="left">Actions</label>', $result);
$this->assertContains('<li class="ss-ui-button dmsdocument-action" data-panel="', $result);
$this->assertContains('permission', $result);
$this->assertContains('Example', $result);
$actions = array('example', 'embargo','find-usage');
foreach ($actions as $action) {
// Test remove with string
$document->removeActionPanelTask($action);
}
$result = $document->getActionTaskHtml();
$this->assertNotContains('Example', $result);
$this->assertNotContains('embargo', $result);
$this->assertNotContains('find-usage', $result);
// Positive test to see some action still remains
$this->assertContains('find-references', $result);
}
/*
* Tests whether the permissions fields are added
*/
public function testGetPermissionsActionPanel()
{
$result = $this->objFromFixture('DMSDocument', 'd1')->getPermissionsActionPanel();
$this->assertInstanceOf('CompositeField', $result);
$this->assertNotNull($result->getChildren()->fieldByName('CanViewType'));
$this->assertNotNull($result->getChildren()->fieldByName('ViewerGroups'));
}
/**
* Test view permissions
*/
public function testCanView()
{
/** @var DMSDocument $document */
$document = $this->objFromFixture('DMSDocument', 'doc-logged-in-users');
$this->logoutMember();
// Logged out user test
$this->assertFalse($document->canView());
// Logged in user test
$adminID = $this->logInWithPermission();
$admin = Member::get()->byID($adminID);
$this->assertTrue($document->canView($admin));
/** @var Member $member */
$admin->logout();
// Check anyone
$document = $this->objFromFixture('DMSDocument', 'doc-anyone');
$this->assertTrue($document->canView());
// Check OnlyTheseUsers
$document = $this->objFromFixture('DMSDocument', 'doc-only-these-users');
$reportAdminID = $this->logInWithPermission('cable-guy');
/** @var Member $reportAdmin */
$reportAdmin = Member::get()->byID($reportAdminID);
$this->assertFalse($document->canView($reportAdmin));
// Add reportAdmin to group
$reportAdmin->addToGroupByCode('content-author');
$this->assertTrue($document->canView($reportAdmin));
$reportAdmin->logout();
}
/**
* Tests edit permissions
*/
public function testCanEdit()
{
$this->logoutMember();
/** @var DMSDocument $document1 */
$document1 = $this->objFromFixture('DMSDocument', 'doc-logged-in-users');
// Logged out user test
$this->assertFalse($document1->canEdit());
//Logged in user test
$contentAuthor = $this->objFromFixture('Member', 'editor');
$this->assertTrue($document1->canEdit($contentAuthor));
// Check OnlyTheseUsers
/** @var DMSDocument $document2 */
$document2 = $this->objFromFixture('DMSDocument', 'doc-only-these-users');
/** @var Member $cableGuy */
$cableGuy = $this->objFromFixture('Member', 'non-editor');
$this->assertFalse($document2->canEdit($cableGuy));
$cableGuy->addToGroupByCode('content-author');
$this->assertTrue($document2->canEdit($cableGuy));
}
/**
* Tests delete permissions
*/
public function testCanDelete()
{
$this->logoutMember();
$document1 = $this->objFromFixture('DMSDocument', 'doc-logged-in-users');
// Logged out user test
$this->assertFalse($document1->canDelete());
// Test editors can delete
$contentAuthor = $this->objFromFixture('Member', 'editor');
$this->assertTrue($document1->canDelete($contentAuthor));
}
/**
* Tests create permission
*/
public function testCanCreate()
{
$this->logoutMember();
$document1 = $this->objFromFixture('DMSDocument', 'doc-logged-in-users');
$this->logInWithPermission('CMS_ACCESS_DMSDocumentAdmin');
// Test CMS access can create
$this->assertTrue($document1->canCreate());
$this->logoutMember();
// Test editors can create
$contentAuthor = $this->objFromFixture('Member', 'editor');
$this->assertTrue($document1->canCreate($contentAuthor));
}
/**
* Logs out any active member
*/
protected function logoutMember()
{
if ($member = Member::currentUser()) {
$member->logOut();
}
}
/**
* Test permission denied reasons for documents
*/
public function testGetPermissionDeniedReason()
{
/** @var DMSDocument $document1 */
$doc1 = $this->objFromFixture('DMSDocument', 'doc-logged-in-users');
$this->assertContains('Please log in to view this document', $doc1->getPermissionDeniedReason());
/** @var DMSDocument $doc2 */
$doc2 = $this->objFromFixture('DMSDocument', 'doc-only-these-users');
$this->assertContains('You are not authorised to view this document', $doc2->getPermissionDeniedReason());
/** @var DMSDocument $doc3 */
$doc3 = $this->objFromFixture('DMSDocument', 'doc-anyone');
$this->assertEquals('', $doc3->getPermissionDeniedReason());
}
/**
* Ensure that all pages that a document belongs to (via many document sets) can be retrieved in one list
*/
public function testGetRelatedPages()
{
$document = $this->objFromFixture('DMSDocument', 'd1');
$result = $document->getRelatedPages();
$this->assertCount(3, $result, 'Document 1 is related to 3 Pages');
$this->assertSame(array('s1', 's2', 's3'), $result->column('URLSegment'));
}
/**
* Test that the title is returned if it is set, otherwise the filename without ID
*/
public function testGetTitleOrFilenameWithoutId()
{
$d1 = $this->objFromFixture('DMSDocument', 'd1');
$this->assertSame('test-file-file-doesnt-exist-1', $d1->getTitle());
$d2 = $this->objFromFixture('DMSDocument', 'd2');
$this->assertSame('File That Doesn\'t Exist (Title)', $d2->getTitle());
}
/**
* Ensure that the folder a document's file is stored in can be retrieved, and that delete() will also delete
* the file and the record
*/
public function testGetStorageFolderThenDelete()
{
Config::inst()->update('DMS', 'folder_name', 'assets/_unit-tests');
$document = DMS::inst()->storeDocument('dms/tests/DMS-test-lorum-file.pdf');
$filename = $document->getStorageFolder() . '/' . $document->getFilename();
$this->assertTrue(file_exists($filename));
$document->delete();
$this->assertFalse($document->exists());
$this->assertFalse(file_exists($filename));
DMSFilesystemTestHelper::delete('assets/_unit-tests');
}
/**
* Test that the link contains an ID and URL slug
*/
public function testGetLink()
{
Config::inst()->update('DMS', 'folder_name', 'assets/_unit-tests');
$document = DMS::inst()->storeDocument('dms/tests/DMS-test-lorum-file.pdf');
$expected = '/dmsdocument/' . $document->ID . '-dms-test-lorum-file-pdf';
$this->assertSame($expected, $document->Link());
$this->assertSame($expected, $document->getLink());
}
/**
* Ensure that the description can be returned in HTML format
*/
public function testGetDescriptionWithLineBreak()
{
$document = DMSDocument::create();
$document->Description = "Line 1\nLine 2\nLine 3";
$document->write();
$this->assertSame("Line 1<br />\nLine 2<br />\nLine 3", $document->getDescriptionWithLineBreak());
}
}

View File

@ -1,24 +1,7 @@
<?php
class DMSEmbargoTest extends SapphireTest
{
public static $fixture_file = "dms/tests/dmsembargotest.yml";
public function tearDownOnce()
{
self::$is_running_test = true;
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
self::$is_running_test = $this->originalIsRunningTest;
}
protected static $fixture_file = 'dmsembargotest.yml';
public function createFakeHTTPRequest($id)
{
@ -29,33 +12,35 @@ class DMSEmbargoTest extends SapphireTest
public function testBasicEmbargo()
{
$oldDMSFolder = DMS::$dmsFolder;
$oldTestMode = DMSDocument_Controller::$testMode;
DMS::$dmsFolder = DMS_DIR; //sneakily setting the DMS folder to the folder where the test file lives
Config::inst()->update('DMS', 'folder_name', 'assets/_unit-test-123');
$doc = new DMSDocument();
$doc->Filename = "DMS-test-lorum-file.pdf";
$doc->Folder = "tests";
$doc = DMS::inst()->storeDocument('dms/tests/DMS-test-lorum-file.pdf');
$doc->CanViewType = 'LoggedInUsers';
$docID = $doc->write();
//fake a request for a document
$controller = new DMSDocument_Controller();
DMSDocument_Controller::$testMode = true;
$result = $controller->index($this->createFakeHTTPRequest($docID));
$this->assertEquals($doc->getFullPath(), $result, "Correct underlying file returned (in test mode)");
$this->assertEquals($doc->getFullPath(), $result, 'Correct underlying file returned (in test mode)');
$doc->embargoIndefinitely();
$this->logInWithPermission('ADMIN');
$result = $controller->index($this->createFakeHTTPRequest($docID));
$this->assertEquals($doc->getFullPath(), $result, "Admins can still download embargoed files");
$this->assertEquals($doc->getFullPath(), $result, 'Admins can still download embargoed files');
$this->logInWithPermission('random-user-group');
$result = $controller->index($this->createFakeHTTPRequest($docID));
$this->assertNotEquals($doc->getFullPath(), $result, "File no longer returned (in test mode) when switching to other user group");
$this->assertNotEquals(
$doc->getFullPath(),
$result,
'File no longer returned (in test mode) when switching to other user group'
);
DMS::$dmsFolder = $oldDMSFolder;
DMSDocument_Controller::$testMode = $oldTestMode;
DMSFilesystemTestHelper::delete('assets/_unit-test-123');
}
public function testEmbargoIndefinitely()
@ -88,7 +73,7 @@ class DMSEmbargoTest extends SapphireTest
$this->assertFalse($doc->isEmbargoed(), "Document is not embargoed");
$this->assertTrue($doc->isExpired(), "Document is expired");
$expireTime = "2019-04-05 11:43:13";
$expireTime = "2069-12-12 17:10:13";
$doc->expireAtDate($expireTime);
$this->assertFalse($doc->isHidden(), "Document is not hidden");
$this->assertFalse($doc->isEmbargoed(), "Document is not embargoed");
@ -129,7 +114,7 @@ class DMSEmbargoTest extends SapphireTest
$this->assertFalse($doc->isEmbargoed(), "Document is not embargoed");
$this->assertFalse($doc->isExpired(), "Document is not expired");
$embargoTime = "2019-04-05 11:43:13";
$embargoTime = "2069-12-12 17:10:13";
$doc->embargoUntilDate($embargoTime);
$this->assertTrue($doc->isHidden(), "Document is hidden");
$this->assertTrue($doc->isEmbargoed(), "Document is embargoed");
@ -157,7 +142,7 @@ class DMSEmbargoTest extends SapphireTest
$doc->Folder = "0";
$dID = $doc->write();
$doc->addPage($s1);
$s1->getDocumentSets()->first()->getDocuments()->add($doc);
$s1->publish('Stage', 'Live');
$s1->doPublish();
@ -192,7 +177,10 @@ class DMSEmbargoTest extends SapphireTest
$s1->publish('Stage', 'Live');
$s1->doPublish();
$doc = DataObject::get_by_id("DMSDocument", $dID);
$this->assertTrue($doc->isHidden(), "Document is still hidden because although the untilPublish flag is cleared, the indefinitely flag is still there");
$this->assertTrue(
$doc->isHidden(),
"Document is still hidden because although the untilPublish flag is cleared, the indefinitely flag is there"
);
$this->assertTrue($doc->isEmbargoed(), "Document is embargoed");
$this->assertFalse($doc->isExpired(), "Document is not expired");

View File

@ -0,0 +1,42 @@
<?php
class DMSFilesystemTestHelper
{
/**
* Files that are added to the DMS asset directory by the DMS instance. They will be removed after running tests.
*
* @var array
*/
protected static $dmsFiles = array('.htaccess', 'web.config');
/**
* Deletes a directory and all files within it, or a file. Will automatically prepend the base path.
*
* This only work while a unit test is running for safety reasons.
*
* @param string $path
*/
public static function delete($path)
{
if (!SapphireTest::is_running_test() || !file_exists($path)) {
return false;
}
$path = BASE_PATH . DIRECTORY_SEPARATOR . $path;
if (is_dir($path)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
$action = $fileinfo->isDir() ? 'rmdir' : 'unlink';
$action($fileinfo->getRealPath());
}
rmdir($path);
} else {
unlink($path);
}
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* Tests DMS shortcode linking functionality.
*
* @package dms
* @subpackage tests
*/
class DMSShortcodeHandlerTest extends SapphireTest
{
protected static $fixture_file = 'dmstest.yml';
public function testShortcodeOperation()
{
Config::inst()->update('DMS', 'folder_name', 'assets/_unit-test-123');
$file = 'dms/tests/DMS-test-lorum-file.pdf';
$document = DMS::inst()->storeDocument($file);
$result = ShortcodeParser::get('default')->parse(sprintf(
'<p><a href="[dms_document_link id=\'%d\']">Document</a></p>',
$document->ID
));
$value = Injector::inst()->create('HTMLValue', $result);
$link = $value->query('//a')->item(0);
$this->assertStringEndsWith(
'/dmsdocument/' . $document->ID . '-dms-test-lorum-file-pdf',
$link->getAttribute('href')
);
$this->assertEquals($document->getExtension(), $link->getAttribute('data-ext'));
$this->assertEquals($document->getFileSizeFormatted(), $link->getAttribute('data-size'));
DMSFilesystemTestHelper::delete('assets/_unit-test-123');
}
/**
* When the document is valid, the correct arguments are provided and some content is given, the content should
* be parsed and added into an anchor to the document
*/
public function testShortcodeWithContentReturnsParsedContentInLink()
{
$document = $this->objFromFixture('DMSDocument', 'd1');
$arguments = array('id' => $document->ID);
$result = DMSShortcodeHandler::handle($arguments, 'Some content', ShortcodeParser::get('default'), '');
$this->assertSame(
'<a href="/dmsdocument/' . $document->ID . '-test-file-file-doesnt-exist-1">Some content</a>',
$result
);
}
/**
* An error page link should be returned if the arguments are not valid, empty or the document is not available etc.
*
* This only applies when an error page with a 404 error code exists.
*/
public function testReturnErrorPageWhenIdIsEmpty()
{
ErrorPage::create(array('URLSegment' => 'testing', 'ErrorCode' => '404'))->write();
$result = DMSShortcodeHandler::handle(array(), '', ShortcodeParser::get('default'), '');
$this->assertContains('testing', $result);
}
/**
* When invalid or no data is available to return from the arguments and no error page exists to use for a link,
* return a blank string
*/
public function testReturnEmptyStringWhenNoErrorPageExistsAndIdIsEmpty()
{
$this->assertSame('', DMSShortcodeHandler::handle(array(), '', ShortcodeParser::get('default'), ''));
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* Tests DMS shortcode linking functionality.
*
* @package dms
* @subpackage tests
*/
class DMSShortcodeTest extends SapphireTest
{
public function testShortcodeOperation()
{
$file = 'dms/tests/DMS-test-lorum-file.pdf';
$document = DMS::inst()->storeDocument($file);
$result = ShortcodeParser::get('default')->parse(sprintf(
'<p><a href="[dms_document_link id=\'%d\']">Document</a></p>', $document->ID
));
$value = Injector::inst()->create('HTMLValue', $result);
$link = $value->query('//a')->item(0);
$this->assertStringEndsWith("/dmsdocument/$document->ID", $link->getAttribute('href'));
$this->assertEquals($document->getExtension(), $link->getAttribute('data-ext'));
$this->assertEquals($document->getFileSizeFormatted(), $link->getAttribute('data-size'));
}
}

View File

@ -1,121 +0,0 @@
<?php
class DMSTagTest extends SapphireTest
{
public function tearDownOnce()
{
self::$is_running_test = true;
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
self::$is_running_test = $this->originalIsRunningTest;
}
public function testAddingTags()
{
$doc = new DMSDocument();
$doc->Filename = "test file";
$doc->Folder = "0";
$doc->write();
$doc->addTag("fruit", "banana");
$doc->addTag("fruit", "orange");
$doc->addTag("fruit", "apple");
$doc->addTag("company", "apple");
$doc->addTag("company", "SilverStripe");
$fruits = $doc->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 3, "3 fruit tags returned");
$this->assertTrue(in_array("banana", $fruits), "correct fruit tags returned");
//sneakily create another document and link one of the tags to that, too
$doc2 = new DMSDocument();
$doc2->Filename = "sneaky file";
$doc2->Folder = "0";
$doc2->write();
$doc2->addTag("fruit", "banana");
$fruits = $doc2->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 1, "Only 1 fruit tags returned");
//tidy up by deleting all tags from doc 1 (But the banana fruit tag should remain)
$doc->removeAllTags();
//banana fruit remains
$fruits = $doc2->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 1, "Only 1 fruit tags returned");
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 1, "A single DMS tag objects remain after deletion of all tags on doc1");
//delete all tags off doc2 to complete the tidy up
$doc2->removeAllTags();
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 0, "No DMS tag objects remain after deletion");
}
public function testRemovingTags()
{
$doc = new DMSDocument();
$doc->Filename = "test file";
$doc->Folder = "0";
$doc->write();
$doc->addTag("fruit", "banana");
$doc->addTag("fruit", "orange");
$doc->addTag("fruit", "apple");
$doc->addTag("company", "apple");
$doc->addTag("company", "SilverStripe");
$companies = $doc->getTagsList("company");
$this->assertNotNull($companies, "Companies returned before deletion");
$this->assertEquals(count($companies), 2, "Two companies returned before deletion");
//delete an entire category
$doc->removeTag("company");
$companies = $doc->getTagsList("company");
$this->assertNull($companies, "All companies deleted");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 3, "Three fruits returned before deletion");
//delete a single tag
$doc->removeTag("fruit", "apple");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 2, "Two fruits returned after deleting one");
//delete a single tag
$doc->removeTag("fruit", "orange");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 1, "One fruits returned after deleting two");
//nothing happens when deleting tag that doesn't exist
$doc->removeTag("fruit", "jellybean");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 1, "One fruits returned after attempting to delete non-existent fruit");
//delete the last fruit
$doc->removeTag("fruit", "banana");
$fruit = $doc->getTagsList("fruit");
$this->assertNull($fruit, "All fruits deleted");
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 0, "No DMS tag objects remain after deletion");
}
}

View File

@ -1,148 +1,183 @@
<?php
class DMSTest extends FunctionalTest
{
protected static $fixture_file = 'dmstest.yml';
/**
* Stub PDF files for testing
*
* @var string
*/
public static $testFile = 'dms/tests/DMS-test-lorum-file.pdf';
public static $testFile2 = 'dms/tests/DMS-test-document-2.pdf';
//store values to reset back to after this test runs
public static $dmsFolderOld;
public static $dmsFolderSizeOld;
/**
* The test folder to write assets into
*
* @var string
*/
protected $testDmsPath = 'assets/_dms-assets-test-1234';
/**
* @var DMSInterace
*/
protected $dms;
/**
* Use a test DMS folder, so we don't overwrite the live one, and clear it out in case of previous broken tests
*
* {@inheritDoc}
*/
public function setUp()
{
parent::setUp();
self::$dmsFolderOld = DMS::$dmsFolder;
self::$dmsFolderSizeOld = DMS::$dmsFolderSize;
//use a test DMS folder, so we don't overwrite the live one
DMS::$dmsFolder = 'dms-assets-test-1234';
//clear out the test folder (in case a broken test doesn't delete it)
$this->delete(BASE_PATH . DIRECTORY_SEPARATOR . 'dms-assets-test-1234');
Config::inst()->update('DMS', 'folder_name', $this->testDmsPath);
DMSFilesystemTestHelper::delete($this->testDmsPath);
$this->dms = DMS::inst();
}
/**
* Delete the test folder after the test runs
*
* {@inheritDoc}
*/
public function tearDown()
{
parent::tearDown();
self::$is_running_test = true;
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
//delete the test folder after the test runs
$this->delete(BASE_PATH . DIRECTORY_SEPARATOR . 'dms-assets-test-1234');
//set the old DMS folder back again
DMS::$dmsFolder = self::$dmsFolderOld;
DMS::$dmsFolderSize = self::$dmsFolderSizeOld;
self::$is_running_test = $this->originalIsRunningTest;
DMSFilesystemTestHelper::delete($this->testDmsPath);
}
public function delete($path)
{
if (file_exists($path) || is_dir($path)) {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($it as $file) {
if (in_array($file->getBasename(), array('.', '..'))) {
continue;
} elseif ($file->isDir()) {
rmdir($file->getPathname());
} elseif ($file->isFile() || $file->isLink()) {
unlink($file->getPathname());
}
}
rmdir($path);
}
}
public function testDMSStorage()
{
$dms = DMS::inst();
$file = self::$testFile;
$document = $dms->storeDocument($file);
$document = $this->dms->storeDocument($file);
$this->assertNotNull($document, "Document object created");
$this->assertTrue(file_exists(DMS::get_dms_path() . DIRECTORY_SEPARATOR . $document->Folder . DIRECTORY_SEPARATOR . $document->Filename), "Document file copied into DMS folder");
//$title = $document->getTag('title');
$this->assertTrue(
file_exists(
DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $document->Folder
. DIRECTORY_SEPARATOR . $document->Filename
),
"Document file copied into DMS folder"
);
}
public function testDMSFolderSpanning()
{
DMS::$dmsFolderSize = 5;
$dms = DMS::inst();
Config::inst()->update('DMS', 'folder_size', 5);
$file = self::$testFile;
$documents = array();
for ($i = 0; $i <= 16; $i++) {
$document = $dms->storeDocument($file);
$document = $this->dms->storeDocument($file);
$this->assertNotNull($document, "Document object created on run number: $i");
$this->assertTrue(file_exists($document->getFullPath()));
$documents[] = $document;
}
//test document objects have their folders set
// Test document objects have their folders set
$folders = array();
for ($i = 0; $i <= 16; $i++) {
$folderName = $documents[$i]->Folder;
$this->assertTrue(strpos($documents[$i]->getFullPath(), DIRECTORY_SEPARATOR . $folderName . DIRECTORY_SEPARATOR) !== false, "Correct folder name for the documents. Document path contains reference to folder name '$folderName'");
$this->assertTrue(
strpos($documents[$i]->getFullPath(), DIRECTORY_SEPARATOR . $folderName . DIRECTORY_SEPARATOR) !== false,
"Correct folder name for the documents. Document path contains reference to folder name '$folderName'"
);
$folders[] = $folderName;
}
//test we created 4 folder to contain the 17 files
// Test we created 4 folder to contain the 17 files
foreach ($folders as $f) {
$this->assertTrue(is_dir(DMS::get_dms_path() . DIRECTORY_SEPARATOR . $f), "Document folder '$f' exists");
$this->assertTrue(
is_dir(DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $f),
"Document folder '$f' exists"
);
}
}
public function testReplaceDocument()
{
$dms = DMS::inst();
//store the first document
$document = $dms->storeDocument(self::$testFile);
// Store the first document
$document = $this->dms->storeDocument(self::$testFile);
$document->Title = "My custom title";
$document->Description = "My custom description";
$document->write();
//then overwrite with a second document
// Then overwrite with a second document
$document = $document->replaceDocument(self::$testFile2);
$this->assertNotNull($document, "Document object created");
$this->assertTrue(file_exists(DMS::get_dms_path() . DIRECTORY_SEPARATOR . $document->Folder . DIRECTORY_SEPARATOR . $document->Filename), "Document file copied into DMS folder");
$this->assertContains("DMS-test-document-2", $document->Filename, "Original document filename is contain in the new filename");
$this->assertTrue(
file_exists(
DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $document->Folder
. DIRECTORY_SEPARATOR . $document->Filename
),
"Document file copied into DMS folder"
);
$this->assertContains(
"DMS-test-document-2",
$document->Filename,
"Original document filename is contain in the new filename"
);
$this->assertEquals("My custom title", $document->Title, "Custom title not modified");
$this->assertEquals("My custom description", $document->Description, "Custom description not modified");
}
public function testDownloadDocument()
/**
* Test that documents can be returned by a given page
*/
public function testGetByPageWithoutEmbargoes()
{
// $dms = DMS::inst();
//
// //store the first document
// $document = $dms->storeDocument(self::$testFile);
// $link = $document->getLink();
//
// Debug::Show($link);
// $d=new DMSDocument_Controller();
// $response = $d->index(new SS_HTTPRequest('GET',$link,array("ID"=>$document->ID)));
// //$response = $this->get($link);
// Debug::show($response);
$pageWithEmbargoes = $this->objFromFixture('SiteTree', 's3');
$documents = $this->dms->getByPage($pageWithEmbargoes);
// Fixture: 6 documents in set, 1 is embargoed
$this->assertCount(5, $documents, 'Embargoed documents are excluded by default');
$this->assertContainsOnlyInstancesOf('DMSDocument', $documents);
}
/**
* Test that embargoed documents are excluded from getByPage
*/
public function testGetByPageWithEmbargoedDocuments()
{
$pageWithEmbargoes = $this->objFromFixture('SiteTree', 's3');
$documents = $this->dms->getByPage($pageWithEmbargoes, true);
// Fixture: 6 documents in set, 1 is embargoed
$this->assertCount(6, $documents, 'Embargoed documents can be included');
$this->assertContainsOnlyInstancesOf('DMSDocument', $documents);
}
/**
* Ensure the shortcode handler key is configurable
*/
public function testShortcodeHandlerKeyIsConfigurable()
{
Config::inst()->update('DMS', 'shortcode_handler_key', 'testing');
$this->assertSame('testing', DMS::inst()->getShortcodeHandlerKey());
}
/**
* Test that document sets can be retrieved for a given page
*/
public function testGetDocumentSetsByPage()
{
$page = $this->objFromFixture('SiteTree', 's1');
$sets = $this->dms->getDocumentSetsByPage($page);
$this->assertCount(2, $sets);
$this->assertContainsOnlyInstancesOf('DMSDocumentSet', $sets);
}
/**
* Ensure that assets/* folders are not included in filesystem sync operations
*/
public function testFolderExcludedFromFilesystemSync()
{
// Undo setup config changes
Config::unnest();
Config::nest();
$result = Filesystem::config()->get('sync_blacklisted_patterns');
$folderName = substr(DMS::config()->get('folder_name'), 7);
$this->assertContains('/^' . $folderName . '$/i', $result);
}
}

View File

@ -1,82 +1,75 @@
<?php
class DMSVersioningTest extends SapphireTest
{
protected $usesDatabase = true;
/**
* Stub PDF files for testing
* @var string
*/
public static $testFile = 'dms/tests/DMS-test-lorum-file.pdf';
public static $testFile2 = 'dms/tests/DMS-test-document-2.pdf';
//store values to reset back to after this test runs
public static $dmsFolderOld;
public static $dmsFolderSizeOld;
/**
* The test folder to write assets into
*
* @var string
*/
protected $testDmsPath = 'assets/_dms-assets-test-versions';
/**
* Store values to reset back to after this test runs
*/
public static $dmsEnableVersionsOld;
/**
* Use a test DMS folder, so we don't overwrite the live one, and clear out the test folder (in case a broken
* test doesn't delete it)
*
* {@inheritDoc}
*/
public function setUp()
{
parent::setUp();
self::$dmsFolderOld = DMS::$dmsFolder;
self::$dmsFolderSizeOld = DMS::$dmsFolderSize;
self::$dmsEnableVersionsOld = DMSDocument_versions::$enable_versions;
DMSDocument_versions::$enable_versions = true;
//use a test DMS folder, so we don't overwrite the live one
DMS::$dmsFolder = 'dms-assets-test-versions';
//clear out the test folder (in case a broken test doesn't delete it)
$this->delete(BASE_PATH . DIRECTORY_SEPARATOR . 'dms-assets-test-versions');
Config::inst()->update('DMS', 'folder_name', $this->testDmsPath);
DMSFilesystemTestHelper::delete($this->testDmsPath);
}
/**
* Delete the test folder after the tests run
*
* {@inheritDoc}
*/
public function tearDown()
{
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
//delete the test folder after the test runs
$this->delete(BASE_PATH . DIRECTORY_SEPARATOR . 'dms-assets-test-versions');
parent::tearDown();
//set the old DMS folder back again
DMS::$dmsFolder = self::$dmsFolderOld;
DMS::$dmsFolderSize = self::$dmsFolderSizeOld;
DMSFilesystemTestHelper::delete($this->testDmsPath);
// Set the old DMS folder back again
DMSDocument_versions::$enable_versions = self::$dmsEnableVersionsOld;
}
public function delete($path)
{
if (file_exists($path) || is_dir($path)) {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($it as $file) {
if (in_array($file->getBasename(), array('.', '..'))) {
continue;
} elseif ($file->isDir()) {
rmdir($file->getPathname());
} elseif ($file->isFile() || $file->isLink()) {
unlink($file->getPathname());
}
}
rmdir($path);
}
}
public function testDMSVersionStorage()
{
$this->markTestSkipped('Needs re-implementation, this test is not consistent.');
$dms = DMS::inst();
$document = $dms->storeDocument(self::$testFile);
$this->assertNotNull($document, "Document object created");
$this->assertTrue(file_exists(DMS::get_dms_path() . DIRECTORY_SEPARATOR . $document->Folder . DIRECTORY_SEPARATOR . $document->Filename), "Document file copied into DMS folder");
$this->assertTrue(
file_exists(
DMS::inst()->getStoragePath() . DIRECTORY_SEPARATOR . $document->Folder
. DIRECTORY_SEPARATOR . $document->Filename
),
"Document file copied into DMS folder"
);
$document->replaceDocument(self::$testFile2);
$document->replaceDocument(self::$testFile);

View File

@ -1,26 +1,25 @@
<?php
class ShortCodeRelationFinderTest extends SapphireTest
{
public static $fixture_file = array(
'dms/tests/dmstest.yml'
);
protected static $fixture_file = 'dmstest.yml';
public function testFindInRate()
{
Config::inst()->update('DMS', 'shortcode_handler_key', 'dms_document_link');
$d1 = $this->objFromFixture('DMSDocument', 'd1');
$d2 = $this->objFromFixture('DMSDocument', 'd2');
$page1 = new SiteTree();
$page1->Content = 'Condition: <a title="document test 1" href="[dms_document_link,id='.$d1->ID.']">';
$page1->Content = 'Condition: <a title="document test 1" href="[dms_document_link,id=' . $d1->ID . ']">';
$page1ID = $page1->write();
$page2 = new SiteTree();
$page2->Content = 'Condition: <a title="document test 2" href="[dms_document_link,id='.$d2->ID.']">';
$page2->Content = 'Condition: <a title="document test 2" href="[dms_document_link,id=' . $d2->ID . ']">';
$page2ID = $page2->write();
$page3 = new SiteTree();
$page3->Content = 'Condition: <a title="document test 1" href="[dms_document_link,id='.$d1->ID.']">';
$page3->Content = 'Condition: <a title="document test 1" href="[dms_document_link,id=' . $d1->ID . ']">';
$page3ID = $page3->write();
$finder = new ShortCodeRelationFinder();

View File

@ -0,0 +1,45 @@
<?php
/**
* Class StubRelatedDocumentExtension
*
* @package dms
*/
class StubRelatedDocumentExtension extends DataExtension implements TestOnly
{
/**
* For method {@link DMSDocument::getRelatedDocuments}
*
* @param ArrayList $relatedDocuments
* @return ArrayList
*/
public function updateRelatedDocuments($relatedDocuments)
{
$relatedDocuments->push($this->getFakeDocument());
return $relatedDocuments;
}
/**
* For method {@link DMSDocumentSet::getDocuments}
*
* @param ArrayList $relatedDocuments
* @return ArrayList
*/
public function updateDocuments($documents)
{
$documents->push($this->getFakeDocument());
return $documents;
}
/**
* Return a dummy document for testing purposes
*
* @return DMSDocument
*/
protected function getFakeDocument()
{
$fakeDocument = new DMSDocument;
$fakeDocument->Filename = 'Extended';
return $fakeDocument;
}
}

View File

@ -0,0 +1,98 @@
<?php
class DMSDocumentAddControllerTest extends FunctionalTest
{
protected static $fixture_file = 'dms/tests/dmstest.yml';
/**
* @var DMSDocumentAddController
*/
protected $controller;
public function setUp()
{
parent::setUp();
$this->logInWithPermission();
$this->controller = new DMSDocumentAddController;
$this->controller->init();
}
/**
* Ensure that if no ID is provided then a SiteTree singleton is returned (which will not have an ID). If one is
* provided then it should be loaded from the database via versioning.
*/
public function testCurrentPageReturnsSiteTree()
{
$page = $this->objFromFixture('SiteTree', 's1');
$this->assertInstanceOf('SiteTree', $this->controller->currentPage());
$this->assertEmpty($this->controller->currentPage()->ID);
$this->controller->setRequest(new SS_HTTPRequest('GET', '/', array('page_id' => $page->ID)));
$this->assertEquals($page->ID, $this->controller->currentPage()->ID, 'Specified page is loaded and returned');
}
/**
* Ensure that if no "dsid" is given a singleton is returned (which will not have an ID). If one is provided
* it should be loaded from the database
*/
public function testGetCurrentDocumentSetReturnsDocumentSet()
{
$set = $this->objFromFixture('DMSDocumentSet', 'ds1');
$this->assertInstanceOf('DMSDocumentSet', $this->controller->getCurrentDocumentSet());
$this->assertEmpty($this->controller->getCurrentDocumentSet()->ID, 'Singleton does not have an ID');
$this->controller->setRequest(new SS_HTTPRequest('GET', '/', array('dsid' => $set->ID)));
$this->assertEquals($set->ID, $this->controller->getCurrentDocumentSet()->ID, 'Specified document set is returned');
}
/**
* Test that extra allowed extensions are merged into the default upload field allowed extensions
*/
public function testGetAllowedExtensions()
{
Config::inst()->remove('File', 'allowed_extensions');
Config::inst()->update('File', 'allowed_extensions', array('jpg', 'gif'));
$this->assertSame(array('jpg', 'gif'), $this->controller->getAllowedExtensions());
Config::inst()->update('DMSDocumentAddController', 'allowed_extensions', array('php', 'php5'));
$this->assertSame(array('jpg', 'gif', 'php', 'php5'), $this->controller->getAllowedExtensions());
}
/**
* Test that the back link will be the document set that a file is uploaded into if relevant, otherwise the model
* admin that it was uploaded from
*/
public function testBacklink()
{
// No page ID and no document set ID
$this->assertContains('admin/documents', $this->controller->Backlink());
// No page ID, has document set ID
$request = new SS_HTTPRequest('GET', '/', array('dsid' => 123));
$this->controller->setRequest($request);
$this->assertContains('EditForm', $this->controller->Backlink());
$this->assertContains('123', $this->controller->Backlink());
// Has page ID and document set ID
$request = new SS_HTTPRequest('GET', '/', array('dsid' => 123, 'page_id' => 234));
$this->controller->setRequest($request);
$this->assertContains('admin/pages', $this->controller->Backlink());
$this->assertContains('123', $this->controller->Backlink());
}
/**
* Test that the document autocomplete endpoint returns JSON, matching on ID, title or filename (case insensitive)
*/
public function testDocumentAutocomplete()
{
$result = (string) $this->get('admin/pages/adddocument/documentautocomplete?term=EXIST')->getBody();
$this->assertJson($result, 'Autocompleter should return JSON');
$this->assertContains("File That Doesn't Exist (Title)", $result);
$this->assertContains('test-file-file-doesnt-exist-1', $result);
$this->assertNotContains('doc-logged-in-users', $result);
$document = $this->objFromFixture('DMSDocument', 'd2');
$result = (string) $this->get('admin/pages/adddocument/documentautocomplete?term=' . $document->ID)->getBody();
$this->assertContains($document->ID . " - File That Doesn't Exist (Title)", $result);
}
}

View File

@ -0,0 +1,22 @@
<?php
class DMSDocumentAddExistingFieldTest extends SapphireTest
{
/**
* The constructor should create a tree dropdown field
*/
public function testFieldContainsTreeDropdownField()
{
$field = new DMSDocumentAddExistingField('Test', 'Test');
$this->assertContainsOnlyInstancesOf('TreeDropdownField', $field->getChildren());
$this->assertSame('PageSelector', $field->getChildren()->first()->getName());
}
public function testSetAndGetRecord()
{
$record = new DMSDocumentSet;
$field = new DMSDocumentAddExistingField('Test');
$field->setRecord($record);
$this->assertSame($record, $field->getRecord());
}
}

View File

@ -0,0 +1,82 @@
<?php
class DMSDocumentAdminTest extends FunctionalTest
{
protected static $fixture_file = 'DMSDocumentAdminTest.yml';
public function setUp()
{
parent::setUp();
$this->logInWithPermission('ADMIN');
}
/**
* Check that the default "add new" and "edit" buttons are gone, and replaced with our customised version of it
*/
public function testGridFieldHasCustomisedButtons()
{
$modelAdmin = new DMSDocumentAdmin;
$modelAdmin->init();
$form = $modelAdmin->getEditForm();
$gridFieldConfig = $form->Fields()->first()->getConfig();
$replacements = array(
'GridFieldAddNewButton'=>'DMSGridFieldAddNewButton',
'GridFieldEditButton'=>'DMSGridFieldEditButton'
);
foreach ($replacements as $oldClass => $newClass) {
// Our button is an instance of the original, so is returned when asking for the original
$newButtons = $gridFieldConfig->getComponentsByType($oldClass);
foreach ($newButtons as $key => $newButton) {
if ($newButton instanceof $newClass) {
// Remove our version for testing's sake
$newButtons->remove($newButton);
}
}
$this->assertCount(0, $newButtons, 'Original button is removed');
$this->assertInstanceOf(
$newClass,
$gridFieldConfig->getComponentByType($newClass),
"Model admin for documents contains customised {$newClass} button"
);
}
}
/**
* Quick check to ensure that the ModelAdmin endpoint is working
*/
public function testModelAdminEndpointWorks()
{
$this->assertEquals(200, $this->get('admin/documents')->getStatusCode());
}
/**
* Check that the document sets GridField has a data column for the parent page title. Here we check for the
* Page title existing in the DOM, since "Page" is guaranteed to exist somewhere else.
*/
public function testDocumentSetsGridFieldHasParentPageColumn()
{
$result = (string) $this->get('admin/documents/DMSDocumentSet')->getBody();
$this->assertContains('Home Test Page', $result);
$this->assertContains('About Us Test Page', $result);
}
/**
* Checks that the document sets GridField has a data column which links to the DocumentSets tab on
* the actual page in the CMS
*/
public function testDocumentSetsGridFieldHasLinkToCMSPageEditor()
{
$result = (string)$this->get('admin/documents/DMSDocumentSet')->getBody();
$this->assertContains(
"<a class='dms-doc-sets-link'",
$result
);
}
}

View File

@ -0,0 +1,32 @@
Page:
home:
Title: Home Test Page
URLSegment: home
about_us:
Title: About Us Test Page
URLSegment: About Us
DMSDocumentSet:
home_default:
Title: Home Default
Page: =>Page.home
home_alt:
Title: Home Alternative
Page: =>Page.home
about_default:
Title: About Default
Page: =>Page.about_us
DMSDocument:
h_d_1:
Title: Home Default 1
Filename: home-default-1
Sets: =>DMSDocumentSet.home_default
h_a_2:
Title: Home Default 2
Filename: home-default-2
Sets: =>DMSDocumentSet.home_alt
a_d_1:
Title: About Default 1
Filename: about-default-1
Sets: =>DMSDocumentSet.about_default

View File

@ -0,0 +1,79 @@
<?php
class DMSGridFieldAddNewButtonTest extends SapphireTest
{
protected static $fixture_file = 'dms/tests/dmstest.yml';
/**
* @var DMSGridFieldAddNewButton
*/
protected $button;
/**
* @var GridField
*/
protected $gridField;
public function setUp()
{
parent::setUp();
$fakeList = DMSDocument::get();
$this->gridField = GridField::create('TestGridField', false, $fakeList);
$this->button = new DMSGridFieldAddNewButton;
}
/**
* Test that when no document set ID is present then it is not added to the URL for "add document"
*/
public function testNoDocumentSetIdInAddUrlWhenNotProvided()
{
$this->assertNotContains('?dsid', $this->getButtonHtml());
}
/**
* Test that when a document set ID is provided, it is added onto the "add document" link
*/
public function testDocumentSetIdAddedToLinkWhenProvided()
{
$this->button->setDocumentSetId(123);
$this->assertContains('?dsid=123', $this->getButtonHtml());
}
/**
* If a set is saved and associated to a page, that page's ID should be added to the "add document" link to help
* to ensure the user gets redirected back to the correct place afterwards
*/
public function testPageIdIsAddedWhenAvailableViaDocumentSetRelationship()
{
$set = $this->objFromFixture('DMSDocumentSet', 'ds1');
$this->button->setDocumentSetId($set->ID);
$controller = new ContentController;
$controller->pushCurrent();
$result = $this->getButtonHtml();
$this->assertContains('dsid=' . $set->ID, $result, 'Add new button contains document set ID');
$this->assertNotContains('page_id=' . $set->Page()->ID, $result, 'No page ID when not editing in page context');
$controller = new CMSPageEditController;
$controller->pushCurrent();
$this->assertContains(
'page_id=' . $set->Page()->ID,
$this->getButtonHtml(),
'Button contains page ID when in edit page context'
);
}
/**
* Returns the HTML result of the "add new" button, used for DRY in test class
*
* @return string
*/
protected function getButtonHtml()
{
$fragments = $this->button->getHTMLFragments($this->gridField);
return array_pop($fragments)->getValue();
}
}

View File

@ -0,0 +1,49 @@
<?php
class DMSUploadFieldTest extends SapphireTest
{
/**
* @var DMSUploadField
*/
protected $field;
public function setUp()
{
parent::setUp();
$this->field = new DMSUploadField('StubUploadField');
}
/**
* SS 3.x injector will return an overloaded parent of a child class if the child is not injected.
* This is a sanity check.
*/
public function testDmsUploadFieldIsInjectable()
{
$this->assertInstanceOf('DMSUploadField', DMSUploadField::create('Stub'));
}
/**
* The validator is coded to always return true. Replace this test if this behaviour changes in future.
*/
public function testValidatorAlwaysReturnsTrue()
{
$this->assertTrue($this->field->validate('foo'));
}
public function testGetItemHandler()
{
$this->assertInstanceOf('DMSUploadField_ItemHandler', $this->field->getItemHandler(123));
}
/**
* Ensure that the folder name can be get/set and that the value set is casted to a string
*/
public function testCanGetAndSetFolderName()
{
$this->field->setFolderName('qwerty');
$this->assertSame('qwerty', $this->field->getFolderName());
$this->field->setFolderName(123);
$this->assertSame('123', $this->field->getFolderName());
}
}

View File

@ -0,0 +1,35 @@
<?php
class DMSUploadField_ItemHandlerTest extends SapphireTest
{
protected static $fixture_file = 'dms/tests/dmstest.yml';
/**
* @var DMSDocument
*/
protected $document;
public function setUp()
{
parent::setUp();
$this->document = $this->objFromFixture('DMSDocument', 'd1');
}
public function testGetItem()
{
$handler = new DMSUploadField_ItemHandler(DMSUploadField::create('Test'), $this->document->ID);
$result = $handler->getItem();
$this->assertSame($result->ID, $this->document->ID, 'getItem returns the correct document from the database');
}
public function testEditForm()
{
$handler = new DMSUploadField_ItemHandler(DMSUploadField::create('Test'), $this->document->ID);
$result = $handler->EditForm();
$this->assertInstanceOf('Form', $result);
$this->assertInstanceOf('DMSDocument', $result->getRecord());
$this->assertSame($this->document->ID, $result->getRecord()->ID);
}
}

View File

@ -1,7 +1,11 @@
DMSDocumentSet:
ds1:
Title: Document Set 1
SiteTree:
s1:
Title: testPage1
URLSegment: s1
DocumentSets: =>DMSDocumentSet.ds1
s2:
Title: testPage2
URLSegment: s2

View File

@ -1,49 +1,134 @@
SiteTree:
s1:
Title: testPage1
Title: testPage1 has document sets
URLSegment: s1
s2:
Title: testPage2
Title: testPage2 a document set
URLSegment: s2
s3:
Title: testPage3
Title: testPage3 has document sets with embargoed docs
URLSegment: s3
s4:
Title: testPage4
Title: testPage4 has no sets
URLSegment: s4
s5:
Title: testPage5
Title: testPage5 has no sets
URLSegment: s5
s6:
Title: testPage6
Title: testPage6 has no sets
URLSegment: s6
DMSTag:
t1:
Category: tag1
Value: tag1value
t2:
Category: tag2
Value: tag2value
t3:
Category: tag3
Value: tag3value
t4:
Category: tag4
Value: tag4value
t5:
Category: tag5
Value: tag5value
t6:
Category: tag6
Value: tag6value
s7:
Title: testPage7 has documents embargoed until publish
URLSegment: s7
s8:
Title: testPage8
URLSegment: s8
s9:
Title: testPage9
URLSegment: s9
Group:
content-author:
Code: content-author
Title: "Content Author"
cable-guy:
Code: cable-guy
Title: "Cable Guy"
Member:
editor:
Name: editor
Groups: =>Group.content-author
non-editor:
Name: cable-guy
Groups: =>Group.cable-guy
DMSDocumentSet:
ds1:
Title: Document Set 1
Page: =>SiteTree.s1
ds2:
Title: Document Set 2
Page: =>SiteTree.s1
ds3:
Title: Document Set 3
Page: =>SiteTree.s2
ds4:
Title: Set containing embargoed documents
Page: =>SiteTree.s3
ds5:
Title: Set containing embargoed until publish documents
Page: =>SiteTree.s7
ds6:
Title: Test Set 6
Page: =>SiteTree.s8
dsSaveLinkedDocuments:
Title: Test Set 6
Page: =>SiteTree.s9
unlinked_set:
Title: Document Set not linked
DMSDocument:
d1:
Filename: test-file-file-doesnt-exist
Filename: test-file-file-doesnt-exist-1
Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Pages: =>SiteTree.s1, =>SiteTree.s2, =>SiteTree.s3, =>SiteTree.s4, =>SiteTree.s5, =>SiteTree.s6
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
d2:
Filename: test-file-file-doesnt-exist
Filename: test-file-file-doesnt-exist-2
Folder: 5
Tags: =>DMSTag.t5, =>DMSTag.t6
Pages: =>SiteTree.s5, =>SiteTree.s6
Title: File That Doesn't Exist (Title)
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
document_with_relations:
Filename: file-with-relations
Folder: 5
RelatedDocuments: =>DMSDocument.d1, =>DMSDocument.d2
doc-logged-in-users:
FileName: doc-logged-in-users
CanViewType: LoggedInUsers
CanEditType: LoggedInUsers
Folder: 5
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
doc-anyone:
FileName: doc-anyone
CanViewType: Anyone
Folder: 5
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
doc-only-these-users:
FileName: doc-only-these-users
CanViewType: OnlyTheseUsers
CanEditType: OnlyTheseUsers
Folder: 5
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
ViewerGroups: =>Group.content-author
EditorGroups: =>Group.content-author
embargoed_document1:
Filename: foobar
EmbargoedIndefinitely: true
Folder: 5
Sets: =>DMSDocumentSet.ds4
embargo_until_publish1:
Filename: embargo-until-publish1
EmbargoUntilPublish: true
Folder: 5
Sets: =>DMSDocumentSet.ds5
embargo_until_publish2:
Filename: embargo-until-publish2
EmbargoUntilPublish: true
Folder: 5
Sets: =>DMSDocumentSet.ds5
extraDoc1:
Filename: extradoc1
Folder: 5
Sets: =>DMSDocumentSet.ds6
ManuallyAdded: 1
CreatedByID: 2
extraDoc2:
Filename: extradoc2
Folder: 5
CreatedByID: 2
ManuallyAdded: 0
extraDoc3:
Filename: extradoc3
Folder: 5
ManuallyAdded: 0
CreatedByID: 2
docSaveLinkedDocuments1:
Filename: saveLinkedDocument1
Folder: 5
Sets: =>DMSDocumentSet.dsSaveLinkedDocuments

View File

@ -0,0 +1,29 @@
<?php
class DMSDocumentTaxonomyExtensionTest extends SapphireTest
{
protected static $fixture_file = 'DMSDocumentTaxonomyExtensionTest.yml';
public function setUp()
{
parent::setUp();
if (!class_exists('TaxonomyType')) {
$this->markTestSkipped('This test requires silverstripe/taxonomy ^1.2 to be installed. Skipping.');
}
}
/**
* Ensure that appropriate tags by taxonomy type are returned, and that their hierarchy is displayd in the title
*/
public function testGetAllTagsMap()
{
$extension = new DMSDocumentTaxonomyExtension;
$result = $extension->getAllTagsMap();
$this->assertContains('Subject > Mathematics', $result);
$this->assertContains('Subject', $result);
$this->assertContains('Subject > Science > Chemistry', $result);
$this->assertNotContains('Physical Education', $result);
}
}

View File

@ -0,0 +1,30 @@
TaxonomyType:
document:
Name: Document
not_a_document:
Name: Not A Document
TaxonomyTerm:
subject:
Name: Subject
Type: =>TaxonomyType.document
maths:
Name: Mathematics
Parent: =>TaxonomyTerm.subject
Type: =>TaxonomyType.document
science:
Name: Science
Parent: =>TaxonomyTerm.subject
Type: =>TaxonomyType.document
chemistry:
Name: Chemistry
Parent: =>TaxonomyTerm.science
Type: =>TaxonomyType.document
chemistry_level_1:
Name: Level 1
Parent: =>TaxonomyTerm.chemistry
Type: =>TaxonomyType.document
# Not applicable to DMS documents, but here for testing edge cases
physed:
Name: Physical Education
Type: =>TaxonomyType.not_a_document

View File

@ -0,0 +1,105 @@
<?php
class DMSSiteTreeExtensionTest extends SapphireTest
{
protected static $fixture_file = 'dms/tests/dmstest.yml';
protected $requiredExtensions = array(
'SiteTree' => array('DMSSiteTreeExtension')
);
/**
* Ensure that setting the configuration property "documents_enabled" to false for a page type will prevent the
* CMS fields from being modified.
*
* Also ensures that a correctly named Document Sets GridField is added to the fields in the right place.
*
* Note: the (1) is the number of sets defined for this SiteTree in the fixture
*
* @dataProvider documentSetEnabledConfigProvider
*/
public function testCanDisableDocumentSetsTab($configSetting, $assertionMethod)
{
Config::inst()->update('SiteTree', 'documents_enabled', $configSetting);
$siteTree = $this->objFromFixture('SiteTree', 's2');
$this->$assertionMethod($siteTree->getCMSFields()->fieldByName('Root.DocumentSets.DocumentSets'));
}
/**
* @return array[]
*/
public function documentSetEnabledConfigProvider()
{
return array(
array(true, 'assertNotNull'),
array(false, 'assertNull')
);
}
/**
* Tests for the Document Sets GridField.
*
* Note: the (1) is the number of sets defined for this SiteTree in the fixture
*/
public function testDocumentSetsGridFieldIsCorrectlyConfigured()
{
Config::inst()->update('SiteTree', 'documents_enabled', true);
$siteTree = $this->objFromFixture('SiteTree', 's2');
$gridField = $siteTree->getCMSFields()->fieldByName('Root.DocumentSets.DocumentSets');
$this->assertInstanceOf('GridField', $gridField);
$this->assertTrue((bool) $gridField->hasClass('documentsets'));
}
/**
* Ensure that a page title can be retrieved with the number of related documents it has (across all document sets).
*
* See fixtures for relationships that define this result.
*/
public function testGetTitleWithNumberOfDocuments()
{
$siteTree = $this->objFromFixture('SiteTree', 's1');
$this->assertSame('testPage1 has document sets (5)', $siteTree->getTitleWithNumberOfDocuments());
}
/**
* Ensure that documents marked as "embargo until publish" are unmarked as such when a page containing them is
* published
*/
public function testOnBeforePublishUnEmbargoesDocumentsSetAsEmbargoedUntilPublish()
{
$siteTree = $this->objFromFixture('SiteTree', 's7');
$siteTree->doPublish();
// Fixture defines this page as having two documents via one set
foreach (array('embargo-until-publish1', 'embargo-until-publish2') as $filename) {
$this->assertFalse(
(bool) $siteTree->getAllDocuments()
->filter('Filename', 'embargo-until-publish1')
->first()
->EmbargoedUntilPublished
);
}
$this->assertCount(0, $siteTree->getAllDocuments()->filter('EmbargoedUntilPublished', true));
}
/**
* Ensure that document sets that are assigned to pages to not show up in "link existing" autocomplete
* search results
*/
public function testGetRelatedDocumentsForAutocompleter()
{
$page = $this->objFromFixture('SiteTree', 's1');
$gridField = $page->getCMSFields()->fieldByName('Root.DocumentSets.DocumentSets');
$this->assertInstanceOf('GridField', $gridField);
$autocompleter = $gridField->getConfig()->getComponentByType('GridFieldAddExistingAutocompleter');
$jsonResult = $autocompleter->doSearch(
$gridField,
new SS_HTTPRequest('GET', '/', array('gridfield_relationsearch' => 'Document Set'))
);
$this->assertContains('Document Set not linked', $jsonResult);
$this->assertNotContains('Document Set 1', $jsonResult);
}
}

View File

@ -0,0 +1,29 @@
<?php
class DMSTaxonomyTypeExtensionTest extends SapphireTest
{
protected $usesDatabase = true;
protected $requiredExtensions = array(
'TaxonomyType' => array('DMSTaxonomyTypeExtension')
);
/**
* Ensure that the configurable list of default records are created
*/
public function testDefaultRecordsAreCreated()
{
Config::inst()->update('DMSTaxonomyTypeExtension', 'default_records', array('Food', 'Beverage', 'Books'));
TaxonomyType::create()->requireDefaultRecords();
$this->assertDOSContains(
array(
array('Name' => 'Food'),
array('Name' => 'Beverage'),
array('Name' => 'Books'),
),
TaxonomyType::get()
);
}
}

View File

@ -0,0 +1,47 @@
<?php
class DMSJsonFieldTest extends SapphireTest
{
public function testJsonFieldConstructorMultiWays()
{
$jsonField = new DMSJsonField('MyJsonField', new FieldList(
new TextField('FirstName', 'Given name'),
new TextField('Surname', 'Last name')
));
$this->assertEquals($jsonField->FieldList()->count(), 2);
$this->assertNotNull($jsonField->FieldList()->dataFieldByName('MyJsonField[FirstName]'));
$jsonField = new DMSJsonField('MyJsonField', array(new TextField('FirstName', 'Given name'),
new TextField('Surname', 'Last name')));
$this->assertEquals($jsonField->FieldList()->count(), 2);
$this->assertNotNull($jsonField->FieldList()->dataFieldByName('MyJsonField[FirstName]'));
$jsonField = new DMSJsonField(
'MyJsonField',
new TextField('FirstName', 'Given name'),
new TextField('Surname', 'Last name')
);
$this->assertEquals($jsonField->FieldList()->count(), 2);
$this->assertNotNull($jsonField->FieldList()->dataFieldByName('MyJsonField[FirstName]'));
}
public function testJsonFieldDataValueCouldDealWithArray()
{
$jsonField = new DMSJsonField('MyJsonField', new FieldList(
new TextField('FirstName', 'Given name'),
new TextField('Surname', 'Last name')
));
$jsonField->setValue($value = array(
'MyJsonField'=>array(
'FirstName' => 'Normann',
'Surname' => 'Lou',
),
));
$this->assertEquals($jsonField->dataValue(), Convert::array2json($value));
$jsonField->setValue($value = array(
'MyJsonField'=>array(),
));
$this->assertNull($jsonField->dataValue());
}
}

View File

@ -0,0 +1,142 @@
<?php
class MigrateToDocumentSetsTaskTest extends SapphireTest
{
protected static $fixture_file = 'MigrateToDocumentSetsTaskTest.yml';
/**
* Ensure that output is formatted either for the CLI or browser
*
* @param bool $isCli
* @param string $expected
* @dataProvider outputProvider
*/
public function testCanOutputToCliOrBrowser($isCli, $expected)
{
$lines = array('Test', 'Test line 2');
$mock = $this->getMockBuilder('MigrateToDocumentSetsTask')
->setMethods(array('isCli'))
->getMock();
$mock->expects($this->exactly(2))
->method('isCli')
->will($this->returnValue($isCli));
ob_start();
foreach ($lines as $line) {
$mock->output($line);
}
$result = ob_get_clean();
$this->assertSame($expected, $result);
}
/**
* @return array[]
*/
public function outputProvider()
{
return array(
array(true, 'Test' . PHP_EOL . 'Test line 2' . PHP_EOL),
array(false, 'Test<br />Test line 2<br />')
);
}
/**
* Ensure that providing an invalid action returns an error
*/
public function testShowErrorOnInvalidAction()
{
$result = $this->runTask(array('action' => 'coffeetime'));
$this->assertContains('Error! Specified action is not valid.', $result);
}
/**
* Test that default document sets can be created for those pages that don't have them already
*/
public function testCreateDefaultDocumentSets()
{
$this->fixtureOldRelations();
$result = $this->runTask(array('action' => 'create-default-document-set'));
$this->assertContains('Finished', $result);
// There are four pages in the fixture, but one of them already has a document set, so should be unchanged
$this->assertContains('Default document set added: 3', $result);
$this->assertContains('Skipped: already has a set: 1', $result);
// Test that some of the relationship records were written correctly
$this->assertCount(1, $firstPageSets = $this->objFromFixture('SiteTree', 'one')->getDocumentSets());
$this->assertSame('Default', $firstPageSets->first()->Title);
$this->assertCount(1, $this->objFromFixture('SiteTree', 'two')->getDocumentSets());
// With dryrun enabled and being run the second time, nothing should be done
$result = $this->runTask(array('action' => 'create-default-document-set', 'dryrun' => '1'));
$this->assertContains('Skipped: already has a set: 4', $result);
$this->assertContains('NOTE: Dryrun mode enabled', $result);
}
/**
* Test that legacy ORM relationship maps are migrated to the new page -> document set -> document relationship
*/
public function testReassignDocumentsToFirstSet()
{
$this->fixtureOldRelations();
// Ensure default sets are created
$this->runTask(array('action' => 'create-default-document-set'));
// Dryrun check
$result = $this->runTask(array('action' => 'reassign-documents', 'dryrun' => '1'));
$this->assertContains('NOTE: Dryrun mode enabled', $result);
$this->assertContains('Reassigned to document set: 3', $result);
// Actual run
$result = $this->runTask(array('action' => 'reassign-documents'));
$this->assertNotContains('NOTE: Dryrun mode enabled', $result);
$this->assertContains('Reassigned to document set: 3', $result);
// Smoke ORM checks
$this->assertCount(1, $this->objFromFixture('SiteTree', 'one')->getAllDocuments());
$this->assertCount(1, $this->objFromFixture('SiteTree', 'two')->getAllDocuments());
$this->assertCount(0, $this->objFromFixture('SiteTree', 'four')->getAllDocuments());
}
/**
* Centralises (slightly) logic for capturing direct output from the task
*
* @param array $getVars
* @return string Task output
*/
protected function runTask(array $getVars)
{
$task = new MigrateToDocumentSetsTask;
$request = new SS_HTTPRequest('GET', '/', $getVars);
ob_start();
$task->run($request);
return ob_get_clean();
}
/**
* Set up the old many many relationship table from documents to pages
*/
protected function fixtureOldRelations()
{
if (!DB::get_schema()->hasTable('DMSDocument_Pages')) {
DB::create_table('DMSDocument_Pages', array(
'DMSDocumentID' => 'int(11) null',
'SiteTreeID' => 'int(11) null'
));
}
$documentIds = $this->getFixtureFactory()->getIds('DMSDocument');
$pageIds = $this->getFixtureFactory()->getIds('SiteTree');
foreach (array('one', 'two', 'three') as $fixtureName) {
$this->getFixtureFactory()->createRaw(
'DMSDocument_Pages',
'rln_' . $fixtureName,
array('DMSDocumentID' => $documentIds[$fixtureName], 'SiteTreeID' => $pageIds[$fixtureName])
);
}
}
}

View File

@ -0,0 +1,22 @@
# Fixtures for migration task testing. The relationships for them are
# created manually in the unit test class.
DMSDocument:
one:
Title: document1
two:
Title: document2
three:
Title: document3
DMSDocumentSet:
four:
Title: documentSet4
SiteTree:
one:
Title: page1
two:
Title: page2
three:
Title: page3
four:
Title: page4
DocumentSets: =>DMSDocumentSet.four

41
webpack.config.js Normal file
View File

@ -0,0 +1,41 @@
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const path = require('path');
const extractSass = new ExtractTextPlugin({
filename: '[name].css',
allChunks: true
});
module.exports = [
{
name: 'css',
entry: {
cmsbundle: './scss/main.scss',
},
output: {
path: path.resolve(__dirname, 'dist/css'),
filename: '[name].css',
},
module: {
rules: [{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: 'css-loader?discardComments'
}, {
loader: 'sass-loader'
}],
fallback: 'style-loader'
})
}, {
test: /\.(jpg|gif|png|svg)$/,
use: [{
loader: 'file-loader?emitFile=false&name=../../[path][name].[ext]'
}]
}]
},
plugins: [
extractSass
]
}
];

3819
yarn.lock Normal file
View File

@ -0,0 +1,3819 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
acorn-dynamic-import@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=
dependencies:
acorn "^4.0.3"
acorn@^4.0.3:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=
acorn@^5.0.0:
version "5.7.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
ajv-keywords@^1.1.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw=
ajv@^4.7.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
dependencies:
co "^4.6.0"
json-stable-stringify "^1.0.1"
ajv@^5.0.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^6.5.5:
version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
dependencies:
fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=
dependencies:
kind-of "^3.0.2"
longest "^1.0.1"
repeat-string "^1.5.2"
alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
dependencies:
micromatch "^3.1.4"
normalize-path "^2.1.1"
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
arr-flatten@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
arr-union@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
array-unique@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
assert@^1.1.1:
version "1.5.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
dependencies:
object-assign "^4.1.1"
util "0.10.3"
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
async-each@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async-foreach@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
async@^2.1.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
dependencies:
lodash "^4.17.14"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
atob@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autoprefixer@^6.3.1:
version "6.7.7"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
integrity sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=
dependencies:
browserslist "^1.7.6"
caniuse-db "^1.0.30000634"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^5.2.16"
postcss-value-parser "^3.2.3"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
dependencies:
chalk "^1.1.3"
esutils "^2.0.2"
js-tokens "^3.0.2"
balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
dependencies:
cache-base "^1.0.1"
class-utils "^0.3.5"
component-emitter "^1.2.1"
define-property "^1.0.0"
isobject "^3.0.1"
mixin-deep "^1.2.0"
pascalcase "^0.1.1"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
big.js@^3.1.3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
block-stream@*:
version "0.0.9"
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=
dependencies:
inherits "~2.0.0"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.9"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
dependencies:
arr-flatten "^1.1.0"
array-unique "^0.3.2"
extend-shallow "^2.0.1"
fill-range "^4.0.0"
isobject "^3.0.1"
repeat-element "^1.1.2"
snapdragon "^0.8.1"
snapdragon-node "^2.0.1"
split-string "^3.0.2"
to-regex "^3.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
dependencies:
buffer-xor "^1.0.3"
cipher-base "^1.0.0"
create-hash "^1.1.0"
evp_bytestokey "^1.0.3"
inherits "^2.0.1"
safe-buffer "^5.0.1"
browserify-cipher@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
dependencies:
browserify-aes "^1.0.4"
browserify-des "^1.0.0"
evp_bytestokey "^1.0.0"
browserify-des@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
dependencies:
cipher-base "^1.0.1"
des.js "^1.0.0"
inherits "^2.0.1"
safe-buffer "^5.1.2"
browserify-rsa@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
dependencies:
bn.js "^4.1.0"
randombytes "^2.0.1"
browserify-sign@^4.0.0:
version "4.0.4"
resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
dependencies:
bn.js "^4.1.1"
browserify-rsa "^4.0.0"
create-hash "^1.1.0"
create-hmac "^1.1.2"
elliptic "^6.0.0"
inherits "^2.0.1"
parse-asn1 "^5.0.0"
browserify-zlib@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
dependencies:
pako "~1.0.5"
browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=
dependencies:
caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7"
buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@^4.3.0:
version "4.9.2"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
isarray "^1.0.0"
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
dependencies:
collection-visit "^1.0.0"
component-emitter "^1.2.1"
get-value "^2.0.6"
has-value "^1.0.0"
isobject "^3.0.1"
set-value "^2.0.0"
to-object-path "^0.3.0"
union-value "^1.0.0"
unset-value "^1.0.0"
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
dependencies:
camelcase "^2.0.0"
map-obj "^1.0.0"
camelcase@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=
camelcase@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
caniuse-api@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
integrity sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=
dependencies:
browserslist "^1.3.6"
caniuse-db "^1.0.30000529"
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30001008"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001008.tgz#1691f28db0e08bf6abb5e472d8aaea392ec4a995"
integrity sha512-Fog+uREPKb/RDc0puoAqqBZB05I8wYff+TIMDkYw9Lweq7+hUEN5fNLpIaBX6AJxq4sndqPct8fYLrDUV6u4xw==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
center-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60=
dependencies:
align-text "^0.1.3"
lazy-cache "^1.0.3"
chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
has-ansi "^2.0.0"
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chokidar@^2.0.2:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
dependencies:
anymatch "^2.0.0"
async-each "^1.0.1"
braces "^2.3.2"
glob-parent "^3.1.0"
inherits "^2.0.3"
is-binary-path "^1.0.0"
is-glob "^4.0.0"
normalize-path "^3.0.0"
path-is-absolute "^1.0.0"
readdirp "^2.2.1"
upath "^1.1.1"
optionalDependencies:
fsevents "^1.2.7"
chownr@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
clap@^1.0.9:
version "1.2.3"
resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51"
integrity sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==
dependencies:
chalk "^1.1.3"
class-utils@^0.3.5:
version "0.3.6"
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
dependencies:
arr-union "^3.1.0"
define-property "^0.2.5"
isobject "^3.0.0"
static-extend "^0.1.1"
cliui@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=
dependencies:
center-align "^0.1.1"
right-align "^0.1.1"
wordwrap "0.0.2"
cliui@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
wrap-ansi "^2.0.0"
clone-deep@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
dependencies:
for-own "^1.0.0"
is-plain-object "^2.0.4"
kind-of "^6.0.0"
shallow-clone "^1.0.0"
clone@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
coa@~1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd"
integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=
dependencies:
q "^1.1.2"
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
dependencies:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=
dependencies:
color-name "^1.0.0"
color@^0.11.0:
version "0.11.4"
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=
dependencies:
clone "^1.0.2"
color-convert "^1.3.0"
color-string "^0.3.0"
colormin@^1.0.5:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
integrity sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=
dependencies:
color "^0.11.0"
css-color-names "0.0.4"
has "^1.0.1"
colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
console-browserify@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
dependencies:
bn.js "^4.1.0"
elliptic "^6.0.0"
create-hash@^1.1.0, create-hash@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
dependencies:
cipher-base "^1.0.1"
inherits "^2.0.1"
md5.js "^1.3.4"
ripemd160 "^2.0.1"
sha.js "^2.4.0"
create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
version "1.1.7"
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0"
inherits "^2.0.1"
ripemd160 "^2.0.0"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cross-spawn@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI=
dependencies:
lru-cache "^4.0.1"
which "^1.2.9"
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
dependencies:
browserify-cipher "^1.0.0"
browserify-sign "^4.0.0"
create-ecdh "^4.0.0"
create-hash "^1.1.0"
create-hmac "^1.1.0"
diffie-hellman "^5.0.0"
inherits "^2.0.1"
pbkdf2 "^3.0.3"
public-encrypt "^4.0.0"
randombytes "^2.0.0"
randomfill "^1.0.3"
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
css-loader@^0.28.1:
version "0.28.11"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7"
integrity sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==
dependencies:
babel-code-frame "^6.26.0"
css-selector-tokenizer "^0.7.0"
cssnano "^3.10.0"
icss-utils "^2.1.0"
loader-utils "^1.0.2"
lodash.camelcase "^4.3.0"
object-assign "^4.1.1"
postcss "^5.0.6"
postcss-modules-extract-imports "^1.2.0"
postcss-modules-local-by-default "^1.2.0"
postcss-modules-scope "^1.1.0"
postcss-modules-values "^1.3.0"
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
css-selector-tokenizer@^0.7.0:
version "0.7.1"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d"
integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==
dependencies:
cssesc "^0.1.0"
fastparse "^1.1.1"
regexpu-core "^1.0.0"
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=
cssnano@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
integrity sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=
dependencies:
autoprefixer "^6.3.1"
decamelize "^1.1.2"
defined "^1.0.0"
has "^1.0.1"
object-assign "^4.0.1"
postcss "^5.0.14"
postcss-calc "^5.2.0"
postcss-colormin "^2.1.8"
postcss-convert-values "^2.3.4"
postcss-discard-comments "^2.0.4"
postcss-discard-duplicates "^2.0.1"
postcss-discard-empty "^2.0.1"
postcss-discard-overridden "^0.1.1"
postcss-discard-unused "^2.2.1"
postcss-filter-plugins "^2.0.0"
postcss-merge-idents "^2.1.5"
postcss-merge-longhand "^2.0.1"
postcss-merge-rules "^2.0.3"
postcss-minify-font-values "^1.0.2"
postcss-minify-gradients "^1.0.1"
postcss-minify-params "^1.0.4"
postcss-minify-selectors "^2.0.4"
postcss-normalize-charset "^1.1.0"
postcss-normalize-url "^3.0.7"
postcss-ordered-values "^2.1.0"
postcss-reduce-idents "^2.2.2"
postcss-reduce-initial "^1.0.0"
postcss-reduce-transforms "^1.0.3"
postcss-svgo "^2.1.1"
postcss-unique-selectors "^2.0.2"
postcss-value-parser "^3.2.3"
postcss-zindex "^2.0.1"
csso@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
integrity sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=
dependencies:
clap "^1.0.9"
source-map "^0.5.3"
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
dependencies:
array-find-index "^1.0.1"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
debug@^2.2.0, debug@^2.3.3:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
define-property@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
dependencies:
is-descriptor "^0.1.0"
define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
dependencies:
is-descriptor "^1.0.0"
define-property@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
dependencies:
is-descriptor "^1.0.2"
isobject "^3.0.1"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
des.js@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
dependencies:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
randombytes "^2.0.0"
domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
electron-to-chromium@^1.2.7:
version "1.3.306"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz#e8265301d053d5f74e36cb876486830261fbe946"
integrity sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A==
elliptic@^6.0.0:
version "6.5.3"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
hmac-drbg "^1.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
enhanced-resolve@^3.3.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.4.0"
object-assign "^4.0.1"
tapable "^0.2.7"
errno@^0.1.3:
version "0.1.7"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
dependencies:
prr "~1.0.1"
error-ex@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
dependencies:
is-arrayish "^0.2.1"
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
esprima@^2.6.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
dependencies:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
dependencies:
debug "^2.3.3"
define-property "^0.2.5"
extend-shallow "^2.0.1"
posix-character-classes "^0.1.0"
regex-not "^1.0.0"
snapdragon "^0.8.1"
to-regex "^3.0.1"
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
dependencies:
is-extendable "^0.1.0"
extend-shallow@^3.0.0, extend-shallow@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
dependencies:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
extglob@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
dependencies:
array-unique "^0.3.2"
define-property "^1.0.0"
expand-brackets "^2.1.4"
extend-shallow "^2.0.1"
fragment-cache "^0.2.1"
regex-not "^1.0.0"
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-text-webpack-plugin@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.2.tgz#756ef4efa8155c3681833fbc34da53b941746d6c"
integrity sha1-dW7076gVXDaBgz+8NNpTuUF0bWw=
dependencies:
async "^2.1.2"
loader-utils "^1.0.2"
schema-utils "^0.3.0"
webpack-sources "^1.0.1"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
fastparse@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
file-loader@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.2.tgz#4ff1df28af38719a6098093b88c82c71d1794a34"
integrity sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==
dependencies:
loader-utils "^1.0.2"
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
dependencies:
extend-shallow "^2.0.1"
is-number "^3.0.0"
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
dependencies:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
flatten@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b"
integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==
for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
for-own@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b"
integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=
dependencies:
for-in "^1.0.1"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
dependencies:
map-cache "^0.2.2"
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
minipass "^2.6.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
fsevents@^1.2.7:
version "1.2.9"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
dependencies:
nan "^2.12.1"
node-pre-gyp "^0.12.0"
fstream@^1.0.0, fstream@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
dependencies:
graceful-fs "^4.1.2"
inherits "~2.0.0"
mkdirp ">=0.5 0"
rimraf "2"
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
has-unicode "^2.0.0"
object-assign "^4.1.0"
signal-exit "^3.0.0"
string-width "^1.0.1"
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gaze@^1.0.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
dependencies:
globule "^1.0.0"
get-caller-file@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
dependencies:
is-glob "^3.1.0"
path-dirname "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
globule@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==
dependencies:
glob "~7.1.1"
lodash "~4.17.10"
minimatch "~3.0.2"
graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.2.3"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.0:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
dependencies:
ajv "^6.5.5"
har-schema "^2.0.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
dependencies:
ansi-regex "^2.0.0"
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
has-value@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
dependencies:
get-value "^2.0.3"
has-values "^0.1.4"
isobject "^2.0.0"
has-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
dependencies:
get-value "^2.0.6"
has-values "^1.0.0"
isobject "^3.0.0"
has-values@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
has-values@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
dependencies:
is-number "^3.0.0"
kind-of "^4.0.0"
has@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
hash-base@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
dependencies:
hash.js "^1.0.3"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
html-comment-regex@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
iconv-lite@^0.4.4:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
icss-replace-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
icss-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
integrity sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=
dependencies:
postcss "^6.0.1"
ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
ignore-walk@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
dependencies:
minimatch "^3.0.4"
in-publish@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
integrity sha1-4g/146KvwmkDILbcVSaCqcf631E=
indent-string@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=
dependencies:
repeating "^2.0.0"
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@~1.3.0:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
interpret@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
is-absolute-url@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
dependencies:
kind-of "^3.0.2"
is-accessor-descriptor@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
dependencies:
kind-of "^6.0.0"
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-binary-path@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
dependencies:
binary-extensions "^1.0.0"
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
dependencies:
kind-of "^3.0.2"
is-data-descriptor@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
dependencies:
kind-of "^6.0.0"
is-descriptor@^0.1.0:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
dependencies:
is-accessor-descriptor "^0.1.6"
is-data-descriptor "^0.1.4"
kind-of "^5.0.0"
is-descriptor@^1.0.0, is-descriptor@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
dependencies:
is-accessor-descriptor "^1.0.0"
is-data-descriptor "^1.0.0"
kind-of "^6.0.2"
is-extendable@^0.1.0, is-extendable@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
is-extendable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
dependencies:
is-plain-object "^2.0.4"
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-finite@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-glob@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
dependencies:
is-extglob "^2.1.0"
is-glob@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
dependencies:
kind-of "^3.0.2"
is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
dependencies:
isobject "^3.0.1"
is-svg@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
integrity sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=
dependencies:
html-comment-regex "^1.1.0"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
dependencies:
isarray "1.0.0"
isobject@^3.0.0, isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-base64@^2.1.8, js-base64@^2.1.9:
version "2.5.1"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
integrity sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=
dependencies:
argparse "^1.0.7"
esprima "^2.6.0"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
json-loader@^0.5.4:
version "0.5.7"
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-stable-stringify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
dependencies:
jsonify "~0.0.0"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
dependencies:
minimist "^1.2.0"
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
dependencies:
is-buffer "^1.1.5"
kind-of@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
dependencies:
is-buffer "^1.1.5"
kind-of@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
lazy-cache@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4=
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
dependencies:
invert-kv "^1.0.0"
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
dependencies:
graceful-fs "^4.1.2"
parse-json "^2.2.0"
pify "^2.0.0"
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
loader-runner@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
loader-utils@^0.2.16:
version "0.2.17"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
dependencies:
big.js "^3.1.3"
emojis-list "^2.0.0"
json5 "^0.5.0"
object-assign "^4.0.1"
loader-utils@^1.0.1, loader-utils@^1.0.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
dependencies:
big.js "^5.2.2"
emojis-list "^2.0.0"
json5 "^1.0.1"
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.tail@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
integrity sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@~4.17.10:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
loud-rejection@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
dependencies:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
map-cache@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
map-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
dependencies:
object-visit "^1.0.0"
math-expression-evaluator@^1.2.14:
version "1.2.17"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw=
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
safe-buffer "^5.1.2"
memory-fs@^0.4.0, memory-fs@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
dependencies:
errno "^0.1.3"
readable-stream "^2.0.1"
meow@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
dependencies:
camelcase-keys "^2.0.0"
decamelize "^1.1.2"
loud-rejection "^1.0.0"
map-obj "^1.0.1"
minimist "^1.1.3"
normalize-package-data "^2.3.4"
object-assign "^4.0.1"
read-pkg-up "^1.0.1"
redent "^1.0.0"
trim-newlines "^1.0.0"
micromatch@^3.1.10, micromatch@^3.1.4:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
dependencies:
arr-diff "^4.0.0"
array-unique "^0.3.2"
braces "^2.3.1"
define-property "^2.0.2"
extend-shallow "^3.0.2"
extglob "^2.0.4"
fragment-cache "^0.2.1"
kind-of "^6.0.2"
nanomatch "^1.2.9"
object.pick "^1.3.0"
regex-not "^1.0.0"
snapdragon "^0.8.1"
to-regex "^3.0.2"
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
mime-db@1.42.0:
version "1.42.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac"
integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.25"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.25.tgz#39772d46621f93e2a80a856c53b86a62156a6437"
integrity sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==
dependencies:
mime-db "1.42.0"
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minizlib@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
minipass "^2.9.0"
mixin-deep@^1.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
dependencies:
for-in "^1.0.2"
is-extendable "^1.0.1"
mixin-object@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=
dependencies:
for-in "^0.1.3"
is-extendable "^0.1.1"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nan@^2.12.1, nan@^2.13.2:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
dependencies:
arr-diff "^4.0.0"
array-unique "^0.3.2"
define-property "^2.0.2"
extend-shallow "^3.0.2"
fragment-cache "^0.2.1"
is-windows "^1.0.2"
kind-of "^6.0.2"
object.pick "^1.3.0"
regex-not "^1.0.0"
snapdragon "^0.8.1"
to-regex "^3.0.1"
needle@^2.2.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
neo-async@^2.5.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
node-gyp@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
dependencies:
fstream "^1.0.0"
glob "^7.0.3"
graceful-fs "^4.1.2"
mkdirp "^0.5.0"
nopt "2 || 3"
npmlog "0 || 1 || 2 || 3 || 4"
osenv "0"
request "^2.87.0"
rimraf "2"
semver "~5.3.0"
tar "^2.0.0"
which "1"
node-libs-browser@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
dependencies:
assert "^1.1.1"
browserify-zlib "^0.2.0"
buffer "^4.3.0"
console-browserify "^1.1.0"
constants-browserify "^1.0.0"
crypto-browserify "^3.11.0"
domain-browser "^1.1.1"
events "^3.0.0"
https-browserify "^1.0.0"
os-browserify "^0.3.0"
path-browserify "0.0.1"
process "^0.11.10"
punycode "^1.2.4"
querystring-es3 "^0.2.0"
readable-stream "^2.3.3"
stream-browserify "^2.0.1"
stream-http "^2.7.2"
string_decoder "^1.0.0"
timers-browserify "^2.0.4"
tty-browserify "0.0.0"
url "^0.11.0"
util "^0.11.0"
vm-browserify "^1.0.1"
node-pre-gyp@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4"
node-sass@^4.13.1:
version "4.13.1"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.1.tgz#9db5689696bb2eec2c32b98bfea4c7a2e992d0a3"
integrity sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
cross-spawn "^3.0.0"
gaze "^1.0.0"
get-stdin "^4.0.1"
glob "^7.0.3"
in-publish "^2.0.0"
lodash "^4.17.15"
meow "^3.7.0"
mkdirp "^0.5.1"
nan "^2.13.2"
node-gyp "^3.8.0"
npmlog "^4.0.0"
request "^2.88.0"
sass-graph "^2.2.4"
stdout-stream "^1.4.0"
"true-case-path" "^1.0.2"
"nopt@2 || 3":
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
dependencies:
abbrev "1"
nopt@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
dependencies:
abbrev "1"
osenv "^0.1.4"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
dependencies:
remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
normalize-url@^1.4.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=
dependencies:
object-assign "^4.0.1"
prepend-http "^1.0.0"
query-string "^4.1.0"
sort-keys "^1.0.0"
npm-bundled@^1.0.1:
version "1.0.6"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
npm-packlist@^1.1.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4"
integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==
dependencies:
ignore-walk "^3.0.1"
npm-bundled "^1.0.1"
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
gauge "~2.7.3"
set-blocking "~2.0.0"
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-copy@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
dependencies:
copy-descriptor "^0.1.0"
define-property "^0.2.5"
kind-of "^3.0.3"
object-visit@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
dependencies:
isobject "^3.0.0"
object.pick@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
dependencies:
isobject "^3.0.1"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
os-browserify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
os-homedir@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
os-locale@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
dependencies:
lcid "^1.0.0"
os-tmpdir@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@0, osenv@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
pako@~1.0.5:
version "1.0.10"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
parse-asn1@^5.0.0:
version "5.1.5"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e"
integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
dependencies:
asn1.js "^4.0.0"
browserify-aes "^1.0.0"
create-hash "^1.1.0"
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
dependencies:
error-ex "^1.2.0"
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
path-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
dependencies:
pinkie-promise "^2.0.0"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
dependencies:
graceful-fs "^4.1.2"
pify "^2.0.0"
pinkie-promise "^2.0.0"
pbkdf2@^3.0.3:
version "3.0.17"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
dependencies:
create-hash "^1.1.2"
create-hmac "^1.1.4"
ripemd160 "^2.0.1"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postcss-calc@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
integrity sha1-d7rnypKK2FcW4v2kLyYb98HWW14=
dependencies:
postcss "^5.0.2"
postcss-message-helpers "^2.0.0"
reduce-css-calc "^1.2.6"
postcss-colormin@^2.1.8:
version "2.2.2"
resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
integrity sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=
dependencies:
colormin "^1.0.5"
postcss "^5.0.13"
postcss-value-parser "^3.2.3"
postcss-convert-values@^2.3.4:
version "2.6.1"
resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
integrity sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=
dependencies:
postcss "^5.0.11"
postcss-value-parser "^3.1.2"
postcss-discard-comments@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
integrity sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=
dependencies:
postcss "^5.0.14"
postcss-discard-duplicates@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
integrity sha1-uavye4isGIFYpesSq8riAmO5GTI=
dependencies:
postcss "^5.0.4"
postcss-discard-empty@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
integrity sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=
dependencies:
postcss "^5.0.14"
postcss-discard-overridden@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
integrity sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=
dependencies:
postcss "^5.0.16"
postcss-discard-unused@^2.2.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
integrity sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=
dependencies:
postcss "^5.0.14"
uniqs "^2.0.0"
postcss-filter-plugins@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec"
integrity sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==
dependencies:
postcss "^5.0.4"
postcss-merge-idents@^2.1.5:
version "2.1.7"
resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
integrity sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=
dependencies:
has "^1.0.1"
postcss "^5.0.10"
postcss-value-parser "^3.1.1"
postcss-merge-longhand@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
integrity sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=
dependencies:
postcss "^5.0.4"
postcss-merge-rules@^2.0.3:
version "2.1.2"
resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
integrity sha1-0d9d+qexrMO+VT8OnhDofGG19yE=
dependencies:
browserslist "^1.5.2"
caniuse-api "^1.5.2"
postcss "^5.0.4"
postcss-selector-parser "^2.2.2"
vendors "^1.0.0"
postcss-message-helpers@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
integrity sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=
postcss-minify-font-values@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
integrity sha1-S1jttWZB66fIR0qzUmyv17vey2k=
dependencies:
object-assign "^4.0.1"
postcss "^5.0.4"
postcss-value-parser "^3.0.2"
postcss-minify-gradients@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
integrity sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=
dependencies:
postcss "^5.0.12"
postcss-value-parser "^3.3.0"
postcss-minify-params@^1.0.4:
version "1.2.2"
resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
integrity sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=
dependencies:
alphanum-sort "^1.0.1"
postcss "^5.0.2"
postcss-value-parser "^3.0.2"
uniqs "^2.0.0"
postcss-minify-selectors@^2.0.4:
version "2.1.1"
resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
integrity sha1-ssapjAByz5G5MtGkllCBFDEXNb8=
dependencies:
alphanum-sort "^1.0.2"
has "^1.0.1"
postcss "^5.0.14"
postcss-selector-parser "^2.0.0"
postcss-modules-extract-imports@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a"
integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==
dependencies:
postcss "^6.0.1"
postcss-modules-local-by-default@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
dependencies:
css-selector-tokenizer "^0.7.0"
postcss "^6.0.1"
postcss-modules-scope@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
dependencies:
css-selector-tokenizer "^0.7.0"
postcss "^6.0.1"
postcss-modules-values@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
dependencies:
icss-replace-symbols "^1.1.0"
postcss "^6.0.1"
postcss-normalize-charset@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
integrity sha1-757nEhLX/nWceO0WL2HtYrXLk/E=
dependencies:
postcss "^5.0.5"
postcss-normalize-url@^3.0.7:
version "3.0.8"
resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
integrity sha1-EI90s/L82viRov+j6kWSJ5/HgiI=
dependencies:
is-absolute-url "^2.0.0"
normalize-url "^1.4.0"
postcss "^5.0.14"
postcss-value-parser "^3.2.3"
postcss-ordered-values@^2.1.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
integrity sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=
dependencies:
postcss "^5.0.4"
postcss-value-parser "^3.0.1"
postcss-reduce-idents@^2.2.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
integrity sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=
dependencies:
postcss "^5.0.4"
postcss-value-parser "^3.0.2"
postcss-reduce-initial@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
integrity sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=
dependencies:
postcss "^5.0.4"
postcss-reduce-transforms@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
integrity sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=
dependencies:
has "^1.0.1"
postcss "^5.0.8"
postcss-value-parser "^3.0.1"
postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
integrity sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=
dependencies:
flatten "^1.0.2"
indexes-of "^1.0.1"
uniq "^1.0.1"
postcss-svgo@^2.1.1:
version "2.1.6"
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
integrity sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=
dependencies:
is-svg "^2.0.0"
postcss "^5.0.14"
postcss-value-parser "^3.2.3"
svgo "^0.7.0"
postcss-unique-selectors@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
integrity sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=
dependencies:
alphanum-sort "^1.0.1"
postcss "^5.0.4"
uniqs "^2.0.0"
postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
postcss-zindex@^2.0.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
integrity sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=
dependencies:
has "^1.0.1"
postcss "^5.0.4"
uniqs "^2.0.0"
postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
version "5.2.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==
dependencies:
chalk "^1.1.3"
js-base64 "^2.1.9"
source-map "^0.5.6"
supports-color "^3.2.3"
postcss@^6.0.1:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
dependencies:
chalk "^2.4.1"
source-map "^0.6.1"
supports-color "^5.4.0"
prepend-http@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.24:
version "1.4.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2"
integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==
public-encrypt@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
dependencies:
bn.js "^4.1.0"
browserify-rsa "^4.0.0"
create-hash "^1.1.0"
parse-asn1 "^5.0.0"
randombytes "^2.0.1"
safe-buffer "^5.1.2"
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qs@~6.5.2:
version "6.5.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
query-string@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s=
dependencies:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
randomfill@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
dependencies:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
dependencies:
deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
dependencies:
find-up "^1.0.0"
read-pkg "^1.0.0"
read-pkg@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
dependencies:
load-json-file "^1.0.0"
normalize-package-data "^2.3.2"
path-type "^1.0.0"
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.3, readable-stream@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readdirp@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
dependencies:
graceful-fs "^4.1.11"
micromatch "^3.1.10"
readable-stream "^2.0.2"
redent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
dependencies:
indent-string "^2.1.0"
strip-indent "^1.0.1"
reduce-css-calc@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=
dependencies:
balanced-match "^0.4.2"
math-expression-evaluator "^1.2.14"
reduce-function-call "^1.0.1"
reduce-function-call@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f"
integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==
dependencies:
balanced-match "^1.0.0"
regenerate@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
dependencies:
extend-shallow "^3.0.2"
safe-regex "^1.1.0"
regexpu-core@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=
dependencies:
regenerate "^1.2.1"
regjsgen "^0.2.0"
regjsparser "^0.1.4"
regjsgen@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=
regjsparser@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=
dependencies:
jsesc "~0.5.0"
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
repeat-element@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
repeat-string@^1.5.2, repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
repeating@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
dependencies:
is-finite "^1.0.0"
request@^2.87.0, request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.0"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.4.3"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.10.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
dependencies:
path-parse "^1.0.6"
ret@~0.1.10:
version "0.1.15"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
right-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8=
dependencies:
align-text "^0.1.1"
rimraf@2, rimraf@^2.6.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
dependencies:
ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sass-graph@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49"
integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=
dependencies:
glob "^7.0.0"
lodash "^4.0.0"
scss-tokenizer "^0.2.3"
yargs "^7.0.0"
sass-loader@^6.0.3:
version "6.0.7"
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.7.tgz#dd2fdb3e7eeff4a53f35ba6ac408715488353d00"
integrity sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==
dependencies:
clone-deep "^2.0.1"
loader-utils "^1.0.1"
lodash.tail "^4.1.1"
neo-async "^2.5.0"
pify "^3.0.0"
sax@^1.2.4, sax@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
schema-utils@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
integrity sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=
dependencies:
ajv "^5.0.0"
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE=
dependencies:
js-base64 "^2.1.8"
source-map "^0.4.2"
"semver@2 || 3 || 4 || 5", semver@^5.3.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
dependencies:
extend-shallow "^2.0.1"
is-extendable "^0.1.1"
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shallow-clone@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==
dependencies:
is-extendable "^0.1.1"
kind-of "^5.0.0"
mixin-object "^2.0.1"
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
dependencies:
define-property "^1.0.0"
isobject "^3.0.0"
snapdragon-util "^3.0.1"
snapdragon-util@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
dependencies:
kind-of "^3.2.0"
snapdragon@^0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
dependencies:
base "^0.11.1"
debug "^2.2.0"
define-property "^0.2.5"
extend-shallow "^2.0.1"
map-cache "^0.2.2"
source-map "^0.5.6"
source-map-resolve "^0.5.0"
use "^3.1.0"
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0=
dependencies:
is-plain-obj "^1.0.0"
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-resolve@^0.5.0:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
dependencies:
atob "^2.1.1"
decode-uri-component "^0.2.0"
resolve-url "^0.2.1"
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
spdx-correct@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
spdx-expression-parse@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.5"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
dependencies:
extend-shallow "^3.0.0"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
dependencies:
define-property "^0.2.5"
object-copy "^0.1.0"
stdout-stream@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==
dependencies:
readable-stream "^2.0.1"
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
stream-http@^2.7.2:
version "2.8.3"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
readable-stream "^2.3.6"
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2":
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string_decoder@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
dependencies:
is-utf8 "^0.2.0"
strip-indent@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
dependencies:
get-stdin "^4.0.1"
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
style-loader@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.17.0.tgz#e8254bccdb7af74bd58274e36107b4d5ab4df310"
integrity sha1-6CVLzNt690vVgnTjYQe01atN8xA=
dependencies:
loader-utils "^1.0.2"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
supports-color@^3.1.0, supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
dependencies:
has-flag "^1.0.0"
supports-color@^5.3.0, supports-color@^5.4.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
svgo@^0.7.0:
version "0.7.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
integrity sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=
dependencies:
coa "~1.0.1"
colors "~1.1.2"
csso "~2.3.1"
js-yaml "~3.7.0"
mkdirp "~0.5.1"
sax "~1.2.1"
whet.extend "~0.9.9"
tapable@^0.2.7, tapable@~0.2.5:
version "0.2.9"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8"
integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==
tar@^2.0.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==
dependencies:
block-stream "*"
fstream "^1.0.12"
inherits "2"
tar@^4:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
timers-browserify@^2.0.4:
version "2.0.11"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
dependencies:
setimmediate "^1.0.4"
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
to-object-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
dependencies:
kind-of "^3.0.2"
to-regex-range@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
dependencies:
is-number "^3.0.0"
repeat-string "^1.6.1"
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
dependencies:
define-property "^2.0.2"
extend-shallow "^3.0.2"
regex-not "^1.0.2"
safe-regex "^1.1.0"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
"true-case-path@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==
dependencies:
glob "^7.1.2"
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
uglify-js@^2.8.27:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0=
dependencies:
source-map "~0.5.1"
yargs "~3.10.0"
optionalDependencies:
uglify-to-browserify "~1.0.0"
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc=
union-value@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
dependencies:
arr-union "^3.1.0"
get-value "^2.0.6"
is-extendable "^0.1.1"
set-value "^2.0.1"
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
unset-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
dependencies:
has-value "^0.3.1"
isobject "^3.0.0"
upath@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
dependencies:
punycode "^2.1.0"
urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
dependencies:
punycode "1.3.2"
querystring "0.2.0"
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
dependencies:
inherits "2.0.1"
util@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
dependencies:
inherits "2.0.3"
uuid@^3.3.2:
version "3.3.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
vendors@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0"
integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
vm-browserify@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
watchpack@^1.3.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==
dependencies:
chokidar "^2.0.2"
graceful-fs "^4.1.2"
neo-async "^2.5.0"
webpack-sources@^1.0.1:
version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
dependencies:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack@^2.5.1:
version "2.7.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.7.0.tgz#b2a1226804373ffd3d03ea9c6bd525067034f6b1"
integrity sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg==
dependencies:
acorn "^5.0.0"
acorn-dynamic-import "^2.0.0"
ajv "^4.7.0"
ajv-keywords "^1.1.1"
async "^2.1.2"
enhanced-resolve "^3.3.0"
interpret "^1.0.0"
json-loader "^0.5.4"
json5 "^0.5.1"
loader-runner "^2.3.0"
loader-utils "^0.2.16"
memory-fs "~0.4.1"
mkdirp "~0.5.0"
node-libs-browser "^2.0.0"
source-map "^0.5.3"
supports-color "^3.1.0"
tapable "~0.2.5"
uglify-js "^2.8.27"
watchpack "^1.3.1"
webpack-sources "^1.0.1"
yargs "^6.0.0"
whet.extend@~0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
integrity sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=
which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
which@1, which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
dependencies:
string-width "^1.0.2 || 2"
window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
dependencies:
string-width "^1.0.1"
strip-ansi "^3.0.1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^3.2.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yargs-parser@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
integrity sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=
dependencies:
camelcase "^3.0.0"
yargs-parser@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
dependencies:
camelcase "^3.0.0"
yargs@^6.0.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=
dependencies:
camelcase "^3.0.0"
cliui "^3.2.0"
decamelize "^1.1.1"
get-caller-file "^1.0.1"
os-locale "^1.4.0"
read-pkg-up "^1.0.1"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^1.0.2"
which-module "^1.0.0"
y18n "^3.2.1"
yargs-parser "^4.2.0"
yargs@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
dependencies:
camelcase "^3.0.0"
cliui "^3.2.0"
decamelize "^1.1.1"
get-caller-file "^1.0.1"
os-locale "^1.4.0"
read-pkg-up "^1.0.1"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^1.0.2"
which-module "^1.0.0"
y18n "^3.2.1"
yargs-parser "^5.0.0"
yargs@~3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=
dependencies:
camelcase "^1.0.2"
cliui "^2.1.0"
decamelize "^1.0.0"
window-size "0.1.0"