diff --git a/.editorconfig b/.editorconfig index 47ae637..dc0ccc6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[{*.yml,package.json}] +[{*.yml,*.scss,*.js,package.json}] indent_size = 2 # The indent size used in the package.json file cannot be changed: diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..4b81cff --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@silverstripe/eslint-config/.eslintrc'); diff --git a/.gitattributes b/.gitattributes index 475f5f2..89eb187 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,4 @@ /.gitignore export-ignore /.travis.yml export-ignore /.scrutinizer.yml export-ignore +/codecov.yml export-ignore diff --git a/.gitignore b/.gitignore index 3b7b7c6..80e016d 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ -.ssh/* -.bash* -.profile /vendor/* .sass-cache/* /node_modules/ +**/*.js.map +**/*.css.map diff --git a/.sass-lint.yml b/.sass-lint.yml new file mode 100644 index 0000000..4cb623f --- /dev/null +++ b/.sass-lint.yml @@ -0,0 +1,179 @@ +# sass-lint config to match the AirBNB style guide +# See silverstripe-admin +files: + include: '**/client/src/**/*.scss' + ignore: + - 'client/src/styles/legacy/*' + - 'src/**/*' +options: + formatter: stylish + merge-default-rules: false +rules: + # Warnings + # Things that require actual refactoring are marked as warnings + class-name-format: + - 1 + - convention: hyphenatedbem + placeholder-name-format: + - 1 + - convention: hyphenatedlowercase + nesting-depth: + - 1 + - max-depth: 3 + no-ids: 1 + no-important: 1 + no-misspelled-properties: + - 1 + - extra-properties: + - "-moz-border-radius-topleft" + - "-moz-border-radius-topright" + - "-moz-border-radius-bottomleft" + - "-moz-border-radius-bottomright" + variable-name-format: + - 1 + - allow-leading-underscore: true + convention: hyphenatedlowercase + no-extends: 1 + + # Warnings: these things are preferential rather than mandatory + no-css-comments: 1 + + # Errors + # Things that can be easily fixed are marked as errors + indentation: + - 2 + - size: 2 + final-newline: + - 2 + - include: true + no-trailing-whitespace: 2 + border-zero: + - 2 + - convention: '0' + brace-style: + - 2 + - allow-single-line: true + clean-import-paths: + - 2 + - filename-extension: false + leading-underscore: false + no-debug: 2 + no-empty-rulesets: 2 + no-invalid-hex: 2 + no-mergeable-selectors: 2 + # no-qualifying-elements: + # - 1 + # - allow-element-with-attribute: false + # allow-element-with-class: false + # allow-element-with-id: false + no-trailing-zero: 2 + no-url-protocols: 2 + quotes: + - 2 + - style: double + space-after-bang: + - 2 + - include: false + space-after-colon: + - 2 + - include: true + space-after-comma: + - 2 + - include: true + space-before-bang: + - 2 + - include: true + space-before-brace: + - 2 + - include: true + space-before-colon: 2 + space-between-parens: + - 2 + - include: false + trailing-semicolon: 2 + url-quotes: 2 + zero-unit: 2 + single-line-per-selector: 2 + one-declaration-per-line: 2 + empty-line-between-blocks: + - 2 + - ignore-single-line-rulesets: true + + + # Missing rules + # There are no sass-lint rules for the following AirBNB style items, but thess + # - Put comments on their own line + # - Put property delcarations before mixins + + # Disabled rules + + # These are other rules that we may wish to consider using in the future + # They are not part of the AirBNB CSS standard but they would introduce some strictness + # bem-depth: 0 + # variable-for-property: 0 + # no-transition-all: 0 + # hex-length: + # - 1 + # - style: short + # hex-notation: + # - 1 + # - style: lowercase + # property-units: + # - 1 + # - global: + # - ch + # - em + # - ex + # - rem + # - cm + # - in + # - mm + # - pc + # - pt + # - px + # - q + # - vh + # - vw + # - vmin + # - vmax + # - deg + # - grad + # - rad + # - turn + # - ms + # - s + # - Hz + # - kHz + # - dpi + # - dpcm + # - dppx + # - '%' + # per-property: {} + # force-attribute-nesting: 1 + # force-element-nesting: 1 + # force-pseudo-nesting: 1 + # function-name-format: + # - 1 + # - allow-leading-underscore: true + # convention: hyphenatedlowercase + # no-color-literals: 1 + # no-duplicate-properties: 1 + # mixin-name-format: + # - 1 + # - allow-leading-underscore: true + # convention: hyphenatedlowercase + # shorthand-values: + # - 1 + # - allowed-shorthands: + # - 1 + # - 2 + # - 3 + # leading-zero: + # - 1 + # - include: false + # no-vendor-prefixes: + # - 1 + # - additional-identifiers: [] + # excluded-identifiers: [] + # placeholder-in-extend: 1 + # no-color-keywords: 2 diff --git a/.travis.yml b/.travis.yml index fc853d7..8b11be3 100755 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: env: DB=PGSQL PHPUNIT_TEST=1 - php: 7.1 env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 + - php: 7.2 + env: DB=MYSQL PHPUNIT_TEST=1 before_script: # Init PHP diff --git a/.upgrade.yml b/.upgrade.yml index 813650c..a93591b 100644 --- a/.upgrade.yml +++ b/.upgrade.yml @@ -20,7 +20,7 @@ mappings: BlogAdminSidebar: SilverStripe\Blog\Forms\BlogAdminSidebar GridFieldAddByDBField: SilverStripe\Blog\Forms\GridField\GridFieldAddByDBField GridFieldBlogPostState: SilverStripe\Blog\Forms\GridField\GridFieldBlogPostState - GridFieldConfig_BlogPost: SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost + GridFieldConfig_BlogPost: SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost BlogArchiveWidget: SilverStripe\Blog\Widgets\BlogArchiveWidget BlogArchiveWidget_Controller: SilverStripe\Blog\Widgets\BlogArchiveWidgetController BlogCategoriesWidget: SilverStripe\Blog\Widgets\BlogCategoriesWidget diff --git a/README.md b/README.md index 800304f..4850edc 100755 --- a/README.md +++ b/README.md @@ -2,12 +2,6 @@ [![Build Status](https://travis-ci.org/silverstripe/silverstripe-blog.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-blog) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-blog/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-blog/?branch=master) [![codecov.io](https://codecov.io/github/silverstripe/silverstripe-blog/coverage.svg?branch=master)](https://codecov.io/github/silverstripe/silverstripe-blog?branch=master) -![helpfulrobot](https://helpfulrobot.io/silverstripe/blog/badge) - -[![Latest Stable Version](https://poser.pugx.org/silverstripe/blog/version)](https://packagist.org/packages/silverstripe/blog) -[![License](https://poser.pugx.org/silverstripe/blog/license)](https://packagist.org/packages/silverstripe/blog) -[![Monthly Downloads](https://poser.pugx.org/silverstripe/blog/d/monthly)](https://packagist.org/packages/silverstripe/blog) - ## Documentation @@ -16,18 +10,18 @@ ## Requirements -``` -silverstripe/cms: ^4.0 -silverstripe/lumberjack: ^2.0 -silverstripe/tagfield: ^2.0 -``` +* SilverStripe CMS 4.0+ +* SilverStripe Lumberjack Module 2.0+ +* SilverStripe Tag Field Module 2.0+ +* SilverStripe Assets 1.0+ +* SilverStripe Asset Admin Module 1.0+ + +Note: this version is compatible with SilverStripe 4. For SilverStripe 3, please see [the 2.x release line](https://github.com/silverstripe/silverstripe-blog/tree/2). ### Suggested Modules -``` -silverstripe/widgets: * -silverstripe/comments: * -``` +* SilverStripe Widgets Module +* SilverStripe Comments Module ## Installation diff --git a/client/dist/js/main.bundle.js b/client/dist/js/main.bundle.js new file mode 100644 index 0000000..3a4be96 --- /dev/null +++ b/client/dist/js/main.bundle.js @@ -0,0 +1,224 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +module.exports = jQuery; + +/***/ }), +/* 1 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_jquery__); + + + +__WEBPACK_IMPORTED_MODULE_0_jquery___default.a.entwine('ss', function ($) { + $('.cms-content-fields > #Form_EditForm_error').entwine({ + 'onadd': function onadd() { + var $target = $('.blog-admin-outer'); + if ($target.length == 1) { + $target.prepend(this); + } + } + }); + + $('.toggle-description').entwine({ + 'onadd': function onadd() { + var $this = $(this); + + if ($this.hasClass('toggle-description-enabled')) { + return; + } + + $this.addClass('toggle-description-enabled'); + + var shown = false; + var $helpInfo = $this.closest('.field').find('.form-text'); + + $this.on('click', function () { + $helpInfo[shown ? 'hide' : 'show'](); + $this.toggleClass('toggle-description-shown'); + shown = !shown; + }); + + $helpInfo.hide(); + + $this.parent().addClass('toggle-description-correct-right'); + $this.parent().prev('.middleColumn').addClass('toggle-description-correct-middle'); + $this.parent().next('.description').addClass('toggle-description-correct-description'); + } + }); + + $('.MergeAction').entwine({ + 'onadd': function onadd() { + var $this = $(this); + + $this.on('click', 'select', function () { + return false; + }); + + $this.children('button').each(function (i, button) { + var $button = $(button); + var $select = $button.prev('select'); + + $button.before(''); + }); + + $this.on('change', 'select', function (e) { + var $target = $(e.target); + + $target.next('input').val($target.val()); + }); + + $this.children('button, select').hide(); + + $this.on('click', '.MergeActionReveal', function (e) { + var $target = $(e.target); + + $target.parent().children('button, select').show(); + $target.hide(); + + return false; + }); + } + }); + + $('.blog-admin-sidebar.cms-panel').entwine({ + MinInnerWidth: 620, + onadd: function onadd() { + this._super(); + this.updateLayout(); + + if (!this.hasClass('collapsed') && $(".blog-admin-outer").width() < this.getMinInnerWidth()) { + this.collapsePanel(); + } + + window.onresize = function () { + this.updateLayout(); + }.bind(this); + }, + togglePanel: function togglePanel(bool, silent) { + this._super(bool, silent); + this.updateLayout(); + }, + + updateLayout: function updateLayout() { + $(this).css('height', '100%'); + var currentHeight = $(this).outerHeight(); + var bottomHeight = $('.cms-content-actions').eq(0).outerHeight(); + $(this).css('height', currentHeight - bottomHeight + "px"); + $(this).css('bottom', bottomHeight + "px"); + + $('.cms-container').updateLayoutOptions({ + minContentWidth: 820 + this.width() + }); + } + }); +}); + +/***/ }), +/* 2 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_jquery___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_jquery__); + + +__WEBPACK_IMPORTED_MODULE_0_jquery___default.a.entwine('ss', function ($) { + $('.add-existing-autocompleter input.text').entwine({ + 'onkeydown': function onkeydown(e) { + if (e.which === 13) { + var $parent = $(this).parents('.add-existing-autocompleter'); + $parent.find('button[type="submit"]').click(); + e.preventDefault(); + return false; + } + } + }); +}); + +/***/ }), +/* 3 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_bundles_cms__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_bundles_gridfieldaddbydbfield__ = __webpack_require__(2); + + + +/***/ }) +/******/ ]); +//# sourceMappingURL=main.bundle.js.map \ No newline at end of file diff --git a/client/dist/styles/main.css b/client/dist/styles/main.css new file mode 100644 index 0000000..7f3b52b --- /dev/null +++ b/client/dist/styles/main.css @@ -0,0 +1,2 @@ +.no-sidebar .content-container.size3of4{width:75%}.blog-entry .post-image img{width:98.75%}.blog-sidebar .WidgetHolder ul{margin-left:0}.blog-sidebar .WidgetHolder ul li,ul.blogTagCloud{list-style-type:none}ul.blogTagCloud{clear:both}ul.blogTagCloud li{float:left;display:inline;padding-right:8px}ul.blogTagCloud li a span{float:left;line-height:30px;text-align:center;padding:0}ul.blogTagCloud .tagCount10{font-size:26pt}ul.blogTagCloud .tagCount9{font-size:24pt}ul.blogTagCloud .tagCount8{font-size:22pt}ul.blogTagCloud .tagCount7{font-size:20pt}ul.blogTagCloud .tagCount6{font-size:18pt}ul.blogTagCloud .tagCount5{font-size:16pt}ul.blogTagCloud .tagCount4{font-size:14pt}ul.blogTagCloud .tagCount3{font-size:12pt}ul.blogTagCloud .tagCount2{font-size:10pt}ul.blogTagCloud .tagCount1{font-size:8pt}#FeaturedImage .middleColumn{clear:none;float:left}.has-panel .cms-content-tools.blog-admin-sidebar{width:280px;border-right:0;border-left:1px solid #c0c0c2;position:absolute!important;right:0;top:0;height:100%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle a{text-align:left;margin:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle.south{border-top:1px solid #aaa}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer{width:100%;padding-right:280px;position:absolute;height:100%;overflow-y:hidden;overflow-x:hidden;-webkit-box-sizing:border-box;box-sizing:border-box}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset{position:relative;overflow:auto;height:100%;width:100%}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title label{float:none}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title input{width:100%;max-width:100%;margin-left:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field+.field{margin-top:10px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .preview{padding-top:0;line-height:25px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .edit{float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.date{width:60%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.time{width:36%;float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn input{width:100%}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer{padding-right:41px}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer #Root_Main{margin-right:15px}.has-panel .cms-content-tools.blog-admin-sidebar.cms-content-tools .cms-panel-content{width:auto}.toggle-description{display:inline-block;font-size:1.2rem;width:20px;height:20px;margin-top:1px;cursor:pointer}.middleColumn.toggle-description-correct-middle{margin-left:0;float:left;width:416px}.tab-content .field p.toggle-description-correct-right{display:inline-block;margin-left:0;padding-left:0;clear:none;float:left}.description.toggle-description-correct-description{width:416px;padding:12px 0}.custom-summary .ui-accordion-content,.custom-summary .ui-accordion-content .field{padding:0}.custom-summary .ui-icon-triangle-1-e{background-position:-16px -128px}.cms table.ss-gridfield-table tr td.MergeAction{width:225px}.cms table.ss-gridfield-table tr td.MergeAction a{display:block;height:100%;width:100%}.cms table.ss-gridfield-table tr td.MergeAction select{width:150px}.cms-content-actions,.cms-preview-controls{z-index:999}.blog-cms-categorisation .toolbar--content{margin-top:0}.blog-cms-categorisation .MergeActionReveal:after{content:"@";font-family:silverstripe;display:inline-block;position:relative;margin-left:10px;top:3px}.blog-cms-categorisation .blog-merge-action{margin-top:5px} +/*# sourceMappingURL=main.css.map*/ \ No newline at end of file diff --git a/images/site-tree-icon.png b/client/images/site-tree-icon.png similarity index 100% rename from images/site-tree-icon.png rename to client/images/site-tree-icon.png diff --git a/client/src/bundles/cms.js b/client/src/bundles/cms.js new file mode 100644 index 0000000..22d076e --- /dev/null +++ b/client/src/bundles/cms.js @@ -0,0 +1,142 @@ +/* global window */ +import jQuery from 'jquery'; + +jQuery.entwine('ss', ($) => { + /** + * The page success/error message sits outside of the html block + * containing the sidebar and cms fields. This means it overflows + * underneath the sidebar. + * + * @see https://github.com/silverstripe/silverstripe-blog/issues/210 + */ + $('.cms-content-fields > #Form_EditForm_error').entwine({ + 'onadd': function () { + var $target = $('.blog-admin-outer'); + if ($target.length == 1) { + $target.prepend(this); + } + } + }); + + /** + * Register expandable help text functions with fields. + */ + $('.toggle-description').entwine({ + 'onadd': function () { + var $this = $(this); + + /** + * Prevent multiple events being added. + */ + if ($this.hasClass('toggle-description-enabled')) { + return; + } + + $this.addClass('toggle-description-enabled'); + + /** + * Toggle next description when button is clicked. + */ + var shown = false; + var $helpInfo = $this.closest('.field').find('.form-text'); + + $this.on('click', function () { + $helpInfo[shown ? 'hide' : 'show'](); + $this.toggleClass('toggle-description-shown'); + shown = !shown; + }); + + /** + * Hide next description by default. + */ + $helpInfo.hide(); + + /** + * Add classes to correct inherited layout issues in a small context. + */ + $this.parent().addClass('toggle-description-correct-right'); + $this.parent().prev('.middleColumn').addClass('toggle-description-correct-middle'); + $this.parent().next('.description').addClass('toggle-description-correct-description'); + } + }); + + /** + * Custom merge actions for tags and categories + */ + $('.MergeAction').entwine({ + 'onadd': function () { + var $this = $(this); + + $this.on('click', 'select', function () { + return false; + }); + + $this.children('button').each(function (i, button) { + var $button = $(button); + var $select = $button.prev('select'); + + $button.before(''); + }); + + $this.on('change', 'select', function (e) { + var $target = $(e.target); + + $target.next('input').val($target.val()); + }); + + $this.children('button, select').hide(); + + $this.on('click', '.MergeActionReveal', function (e) { + var $target = $(e.target); + + $target.parent().children('button, select').show(); + $target.hide(); + + return false; + }); + } + }); + + /** + * Customise the cms-panel behaviour for blog sidebar + * + * see LeftAndMain.Panel.js for base behaviour + */ + $('.blog-admin-sidebar.cms-panel').entwine({ + MinInnerWidth: 620, + onadd: function () { + this._super(); + this.updateLayout(); + + // If this panel is open and the left hand column is smaller than the minimum, contract it instead + if (!this.hasClass('collapsed') && ($(".blog-admin-outer").width() < this.getMinInnerWidth())) { + this.collapsePanel(); + } + + window.onresize = function () { + this.updateLayout(); + }.bind(this); + }, + togglePanel: function (bool, silent) { + this._super(bool, silent); + this.updateLayout(); + }, + /** + * Adjust minimum width of content to account for extra panel + * + * @returns {undefined} + */ + updateLayout: function () { + $(this).css('height', '100%'); + var currentHeight = $(this).outerHeight(); + var bottomHeight = $('.cms-content-actions').eq(0).outerHeight(); + $(this).css('height', (currentHeight - bottomHeight) + "px"); + $(this).css('bottom', bottomHeight + "px"); + + $('.cms-container').updateLayoutOptions({ + minContentWidth: 820 + this.width() + }); + + } + }); +}); diff --git a/client/src/bundles/gridfieldaddbydbfield.js b/client/src/bundles/gridfieldaddbydbfield.js new file mode 100644 index 0000000..4dbc331 --- /dev/null +++ b/client/src/bundles/gridfieldaddbydbfield.js @@ -0,0 +1,17 @@ +import jQuery from 'jquery'; + +jQuery.entwine('ss', ($) => { + /** + * Prevent the CMS hijacking the return key + */ + $('.add-existing-autocompleter input.text').entwine({ + 'onkeydown': function (e) { + if (e.which === 13) { + const $parent = $(this).parents('.add-existing-autocompleter'); + $parent.find('button[type="submit"]').click(); + e.preventDefault(); + return false; + } + } + }); +}); diff --git a/client/src/main.js b/client/src/main.js new file mode 100644 index 0000000..d71c17a --- /dev/null +++ b/client/src/main.js @@ -0,0 +1,2 @@ +import 'bundles/cms'; +import 'bundles/gridfieldaddbydbfield'; diff --git a/client/src/main.scss b/client/src/main.scss new file mode 100644 index 0000000..471ecdb --- /dev/null +++ b/client/src/main.scss @@ -0,0 +1,2 @@ +@import "styles/blog"; +@import "styles/cms"; diff --git a/client/src/styles/blog.scss b/client/src/styles/blog.scss new file mode 100644 index 0000000..5f409f7 --- /dev/null +++ b/client/src/styles/blog.scss @@ -0,0 +1,76 @@ +.no-sidebar .content-container.size3of4 { + width: 75%; +} + +.blog-entry .post-image img { + width: 98.75%; +} + +.blog-sidebar .WidgetHolder ul { + margin-left: 0; + + li { + list-style-type: none; + } +} + +// tag cloud related +$base_tag_font_size: 4pt; + +ul.blogTagCloud { + list-style-type: none; + clear: both; + + li { + float: left; + display: inline; + padding-right: 8px; + + a span { + float: left; + line-height: 30px; + text-align: center; + padding: 0; + } + } + + .tagCount10 { + font-size: $base_tag_font_size + 22pt; + } + + .tagCount9 { + font-size: $base_tag_font_size + 20pt; + } + + .tagCount8 { + font-size: $base_tag_font_size + 18pt; + } + + .tagCount7 { + font-size: $base_tag_font_size + 16pt; + } + + .tagCount6 { + font-size: $base_tag_font_size + 14pt; + } + + .tagCount5 { + font-size: $base_tag_font_size + 12pt; + } + + .tagCount4 { + font-size: $base_tag_font_size + 10pt; + } + + .tagCount3 { + font-size: $base_tag_font_size + 8pt; + } + + .tagCount2 { + font-size: $base_tag_font_size + 6pt; + } + + .tagCount1 { + font-size: $base_tag_font_size + 4pt; + } +} diff --git a/client/src/styles/cms.scss b/client/src/styles/cms.scss new file mode 100755 index 0000000..f4fcd7f --- /dev/null +++ b/client/src/styles/cms.scss @@ -0,0 +1,190 @@ +/** + * CMS Styles + */ + +#FeaturedImage .middleColumn { + clear: none; + float: left; +} + +.has-panel .cms-content-tools.blog-admin-sidebar { + width: 280px; + border-right: 0; + border-left: 1px solid #C0C0C2; + position: absolute !important; /* overrides cms !imporant style */ + right: 0; + top: 0; + height: 100%; + + .cms-panel-toggle a { + text-align: left; + margin: 0; + } + + .cms-panel-toggle.south { + border-top: 1px solid #aaaaaa; + } + + ~ .blog-admin-outer { + width: 100%; + padding-right: 280px; + position: absolute; + height: 100%; + overflow-y: hidden; + overflow-x: hidden; + box-sizing: border-box; + + > .ss-tabset { + position: relative; + overflow: auto; + height: 100%; + width: 100%; + + #Title { + label { + float: none; + } + + .middleColumn, + input { + width: 100%; + max-width: 100%; + margin-left: 0; + } + } + } + } + + .cms-content-view { + > .field { + + .field { + margin-top: 10px; + } + + &.urlsegment { + .preview { + padding-top: 0; + line-height: 25px; + } + + .edit { + float: right; + } + } + + &.datetime { + > .middleColumn { + > .date { + width: 60%; + } + + > .time { + width: 36%; + float: right; + } + + .middleColumn, + input { + width: 100%; + } + } + } + } + } + + &.collapsed { + ~ .blog-admin-outer { + padding-right: 41px; + + #Root_Main { + margin-right: 15px; + } + } + } + + &.cms-content-tools { + .cms-panel-content { + width: auto; + } + } +} + +.toggle-description { + display: inline-block; + font-size: 1.2rem; + width: 20px; + height: 20px; + margin-top: 1px; + cursor: pointer; +} + +.middleColumn.toggle-description-correct-middle { + margin-left: 0; + float: left; + width: 416px; +} + +.tab-content .field p.toggle-description-correct-right { + display: inline-block; + margin-left: 0; + padding-left: 0; + clear: none; + float: left; +} + +.description.toggle-description-correct-description { + width: 416px; + padding: 12px 0; +} + +.custom-summary { + .ui-accordion-content, + .ui-accordion-content .field { + padding: 0; + } + + // Change the caret to a plus icon + .ui-icon-triangle-1-e { + background-position: -16px -128px; + } +} + +.cms table.ss-gridfield-table { + tr td.MergeAction { + width: 225px; + + a { + display: block; + height: 100%; + width: 100%; + } + + select { + width: 150px; + } + } +} + +.cms-content-actions, +.cms-preview-controls { + z-index: 999; +} + +.blog-cms-categorisation { + .toolbar--content { + margin-top: 0; + } + + .MergeActionReveal:after { + content: "@"; + font-family: silverstripe; + display: inline-block; + position: relative; + margin-left: 10px; + top: 3px; + } + + .blog-merge-action { + margin-top: 5px; + } +} diff --git a/composer.json b/composer.json index b1417c6..51a18e5 100755 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "blog", "news" ], - "type": "silverstripe-module", + "type": "silverstripe-vendormodule", "require": { "silverstripe/cms": "^4.0", "silverstripe/lumberjack": "^2.0", @@ -21,7 +21,11 @@ "extra": { "branch-alias": { "dev-master": "3.0.x-dev" - } + }, + "expose": [ + "client/dist", + "client/images" + ] }, "autoload": { "psr-4": { diff --git a/config.rb b/config.rb deleted file mode 100644 index c06ea53..0000000 --- a/config.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'compass/import-once/activate' -# Require any additional compass plugins here. - -# Set this to the root of your project when deployed: -http_path = "/" -css_dir = "css" -sass_dir = "scss" -images_dir = "images" -javascripts_dir = "javascripts" - -# You can select your preferred output style here (can be overridden via the command line): -output_style = :nested - -# To enable relative paths to assets via compass helper functions. Uncomment: -# relative_assets = true - -# To disable debugging comments that display the original location of your selectors. Uncomment: -line_comments = false - - -# If you prefer the indented syntax, you might want to regenerate this -# project again passing --syntax sass, or you can uncomment this: -# preferred_syntax = :sass -# and then run: -# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass diff --git a/css/blog.css b/css/blog.css deleted file mode 100644 index a89a5eb..0000000 --- a/css/blog.css +++ /dev/null @@ -1,43 +0,0 @@ -.no-sidebar .content-container.size3of4 { - width: 75%; } - -.blog-entry .post-image img { - width: 98.75%; } - -.blog-sidebar .WidgetHolder ul { - margin-left: 0; } - .blog-sidebar .WidgetHolder ul li { - list-style-type: none; } - -ul.blogTagCloud { - list-style-type: none; - clear: both; } - ul.blogTagCloud li { - float: left; - display: inline; - padding-right: 8px; } - ul.blogTagCloud li a span { - float: left; - line-height: 30px; - text-align: center; - padding: 0px; } - ul.blogTagCloud .tagCount10 { - font-size: 26pt; } - ul.blogTagCloud .tagCount9 { - font-size: 24pt; } - ul.blogTagCloud .tagCount8 { - font-size: 22pt; } - ul.blogTagCloud .tagCount7 { - font-size: 20pt; } - ul.blogTagCloud .tagCount6 { - font-size: 18pt; } - ul.blogTagCloud .tagCount5 { - font-size: 16pt; } - ul.blogTagCloud .tagCount4 { - font-size: 14pt; } - ul.blogTagCloud .tagCount3 { - font-size: 12pt; } - ul.blogTagCloud .tagCount2 { - font-size: 10pt; } - ul.blogTagCloud .tagCount1 { - font-size: 8pt; } diff --git a/css/categories-tags.css b/css/categories-tags.css deleted file mode 100644 index e69de29..0000000 diff --git a/css/cms.css b/css/cms.css deleted file mode 100755 index beb7395..0000000 --- a/css/cms.css +++ /dev/null @@ -1,126 +0,0 @@ -/** - * CMS Styles - */ -/** - * Include Compass framework - */ -/* - * Sprite maps & Icons - */ -#FeaturedImage .middleColumn { - clear: none; - float: left; } - -.has-panel .cms-content-tools.blog-admin-sidebar { - width: 280px; - border-right: none; - border-left: 1px solid #C0C0C2; - position: absolute !important; - /* overrides cms !imporant style */ - right: 0px; - top: 0; - height: 100%; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle a { - text-align: left; - margin: 0; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle.south { - border-top: 1px solid #aaaaaa; } - .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer { - width: 100%; - padding-right: 280px; - position: absolute; - height: 100%; - overflow-y: hidden; - overflow-x: hidden; - box-sizing: border-box; } - .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset { - position: relative; - overflow: auto; - height: 100%; - width: 100%; } - .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title label { - float: none; } - .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title .middleColumn, .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title input { - width: 100%; - max-width: 100%; - margin-left: 0; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field + .field { - margin-top: 10px; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.urlsegment .preview { - padding-top: 0; - line-height: 25px; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.urlsegment .edit { - float: right; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .date { - width: 60%; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .time { - width: 36%; - float: right; } - .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn .middleColumn, .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn input { - width: 100%; } - .has-panel .cms-content-tools.blog-admin-sidebar.collapsed ~ .blog-admin-outer { - padding-right: 41px; } - .has-panel .cms-content-tools.blog-admin-sidebar.collapsed ~ .blog-admin-outer #Root_Main { - margin-right: 15px; } - .has-panel .cms-content-tools.blog-admin-sidebar.cms-content-tools .cms-panel-content { - width: auto; } - -.toggle-description { - text-indent: -1000000px; - display: inline-block; - background: url("../images/information.png") no-repeat center center; - width: 20px; - height: 20px; - margin-left: 4px; - cursor: pointer; } - -.middleColumn.toggle-description-correct-middle { - margin-left: 0; - float: left; - width: 416px; } - -.tab-content .field p.toggle-description-correct-right { - display: inline-block; - margin-left: 0; - padding-left: 0; - clear: none; - float: left; } - -.description.toggle-description-correct-description { - width: 416px; - padding: 12px 0; } - -.custom-summary .ui-accordion-content .field { - margin: 0; } -.custom-summary .ui-accordion-content, -.custom-summary .ui-accordion-content .field { - padding: 0; } -.custom-summary .ui-icon-triangle-1-e { - background-position: -16px -128px; } - -.cms table.ss-gridfield-table tr td.MergeAction { - width: 225px; } - .cms table.ss-gridfield-table tr td.MergeAction a { - display: block; - height: 100%; - width: 100%; } - .cms table.ss-gridfield-table tr td.MergeAction select { - width: 150px; } - -.cms-content-actions, -.cms-preview-controls { - z-index: 999; } - -.blog-cms-categorisation .MergeActionReveal { - margin-left: 10px; } -.blog-cms-categorisation .toolbar--content { - margin-top: 0; } -.blog-cms-categorisation .MergeActionReveal:after { - content: ''; - background: url("../images/move-icon.png"); - display: inline-block; - height: 16px; - width: 16px; - margin-left: 4px; } -.blog-cms-categorisation button.action { - margin-left: 5px; } diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 819222d..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,17 +0,0 @@ -var sass = require("gulp-sass"); -var gulp = require("gulp"); -var watch = require('gulp-watch'); - -gulp.task("scss", function () { - gulp.src("./scss/*.scss") - .pipe(sass().on('error', sass.logError)) - .pipe(gulp.dest("./css")); -}); - -gulp.task('watch', ['scss'], function () { - gulp.watch('./scss/*.scss', ['scss']); -}); - -gulp.task('default', ['scss'], function () { - // noop -}); diff --git a/images/blog-icon-s0a5ab5f851.png b/images/blog-icon-s0a5ab5f851.png deleted file mode 100644 index 1668f7a..0000000 Binary files a/images/blog-icon-s0a5ab5f851.png and /dev/null differ diff --git a/images/information.png b/images/information.png deleted file mode 100755 index 12cd1ae..0000000 Binary files a/images/information.png and /dev/null differ diff --git a/images/move-icon.png b/images/move-icon.png deleted file mode 100644 index eac4f24..0000000 Binary files a/images/move-icon.png and /dev/null differ diff --git a/js/cms.js b/js/cms.js deleted file mode 100644 index d73f70c..0000000 --- a/js/cms.js +++ /dev/null @@ -1,144 +0,0 @@ -(function ($) { - - $.entwine('ss', function ($) { - - /** - * The page success/error message sits outside of the html block - * containing the sidebar and cms fields. This means it overflows - * underneath the sidebar. - * - * @see https://github.com/silverstripe/silverstripe-blog/issues/210 - */ - $('.cms-content-fields > #Form_EditForm_error').entwine({ - 'onadd': function () { - var $target = $('.blog-admin-outer'); - if ($target.length == 1) { - $target.prepend(this); - } - } - }); - - /** - * Register expandable help text functions with fields. - */ - $('.toggle-description').entwine({ - 'onadd': function () { - var $this = $(this); - - /** - * Prevent multiple events being added. - */ - if ($this.hasClass('toggle-description-enabled')) { - return; - } - - $this.addClass('toggle-description-enabled'); - - /** - * Toggle next description when button is clicked. - */ - var shown = false; - var $helpInfo = $this.closest('.field').find('.form-text'); - - $this.on('click', function () { - $helpInfo[shown ? 'hide' : 'show'](); - $this.toggleClass('toggle-description-shown'); - shown = !shown; - }); - - /** - * Hide next description by default. - */ - $helpInfo.hide(); - - /** - * Add classes to correct inherited layout issues in a small context. - */ - $this.parent().addClass('toggle-description-correct-right'); - $this.parent().prev('.middleColumn').addClass('toggle-description-correct-middle'); - $this.parent().next('.description').addClass('toggle-description-correct-description'); - } - }); - - /** - * Custom merge actions for tags and categories - */ - $('.MergeAction').entwine({ - 'onadd': function () { - var $this = $(this); - - $this.on('click', 'select', function () { - return false; - }); - - $this.children('button').each(function (i, button) { - var $button = $(button); - var $select = $button.prev('select'); - - $button.before(''); - }); - - $this.on('change', 'select', function (e) { - var $target = $(e.target); - - $target.next('input').val($target.val()); - }); - - $this.children('button, select').hide(); - - $this.on('click', '.MergeActionReveal', function (e) { - var $target = $(e.target); - - $target.parent().children('button, select').show(); - $target.hide(); - - return false; - }); - } - }); - - /** - * Customise the cms-panel behaviour for blog sidebar - * - * see LeftAndMain.Panel.js for base behaviour - */ - $('.blog-admin-sidebar.cms-panel').entwine({ - MinInnerWidth: 620, - onadd: function () { - this._super(); - this.updateLayout(); - - // If this panel is open and the left hand column is smaller than the minimum, contract it instead - if (!this.hasClass('collapsed') && ($(".blog-admin-outer").width() < this.getMinInnerWidth())) { - this.collapsePanel(); - } - - window.onresize = function () { - this.updateLayout(); - }.bind(this); - }, - togglePanel: function (bool, silent) { - this._super(bool, silent); - this.updateLayout(); - }, - /** - * Adjust minimum width of content to account for extra panel - * - * @returns {undefined} - */ - updateLayout: function () { - $(this).css('height', '100%'); - var currentHeight = $(this).outerHeight(); - var bottomHeight = $('.cms-content-actions').eq(0).outerHeight(); - $(this).css('height', (currentHeight - bottomHeight) + "px"); - $(this).css('bottom', bottomHeight + "px"); - - $('.cms-container').updateLayoutOptions({ - minContentWidth: 820 + this.width() - }); - - } - }); - - }); -})(jQuery); diff --git a/js/gridfieldaddbydbfield.js b/js/gridfieldaddbydbfield.js deleted file mode 100644 index 67799f3..0000000 --- a/js/gridfieldaddbydbfield.js +++ /dev/null @@ -1,16 +0,0 @@ -(function ($) { - $.entwine('ss', function ($) { - /** - * Prevent the CMS hijacking the return key - */ - $('.add-existing-autocompleter input.text').entwine({ - 'onkeydown': function (e) { - if (e.which == 13) { - $parent = $(this).parents('.add-existing-autocompleter'); - $parent.find('button[type="submit"]').click(); - return false; - } - } - }); - }); -})(jQuery); diff --git a/lang/en.yml b/lang/en.yml old mode 100755 new mode 100644 index 857dd25..dada3a4 --- a/lang/en.yml +++ b/lang/en.yml @@ -41,6 +41,9 @@ en: Tag: Tag Tagged: Tagged Tags: Tags + UsersContributorsFieldDescription: "Contributors have the ability to create or edit BlogPosts, but are unable to publish without \n authorisation of an editor. They are also unable to assign other contributing authors to any of\n their BlogPosts.
\n
\n Contributors have these permissions:
\n
\n Update any BlogPost they have authored or have been assigned to" + UsersEditorsFieldDescription: "An editor has control over specific Blogs, and all posts included within it. \n Short of being able to assign other editors to a blog, they are able to handle most changes to\n their assigned blog.

\n Editors have these permissions:
\n
\n Update or publish any BlogPost in their Blog
\n Update or publish their Blog
\n Assign/unassign writers to their Blog
\n Assign/unassign contributors to their Blog
\n Assign/unassign any member as an author of a particular BlogPost" + UsersWritersFieldDescription: "A writer has full control over creating, editing and publishing BlogPosts they have authored\n or have been assigned to. Writers are unable to edit BlogPosts to which they are not assigned.\n

\n Writers have these permissions:
\n
\n Update or publish any BlogPost they have authored or have been assigned to
\n Assign/unassign any member as an author of a particular BlogPost they have authored or have been \n assigned to" WRITER: Writer SilverStripe\Blog\Model\BlogCategory: Duplicate: 'A blog category already exists with that name.' @@ -81,24 +84,49 @@ en: ArchiveType: ArchiveType Blog: Blog NumberToDisplay: 'No. to Display' + PLURALNAME: 'Blog Archive Widgets' + PLURALS: + one: 'A Blog Archive Widget' + other: '{count} Blog Archive Widgets' + SINGULARNAME: 'Blog Archive Widget' SilverStripe\Blog\Widgets\BlogCategoriesWidget: Blog: Blog Direction: Direction Direction_Description: 'Change the direction of ordering of categories shown by this widget.' Limit: Limit Limit_Description: 'Limit the number of categories shown by this widget (set to 0 to show all categories).' + PLURALNAME: 'Blog Categories Widgets' + PLURALS: + one: 'A Blog Categories Widget' + other: '{count} Blog Categories Widgets' + SINGULARNAME: 'Blog Categories Widget' Sort: Sort Sort_Description: 'Change the order of categories shown by this widget.' SilverStripe\Blog\Widgets\BlogRecentPostsWidget: Blog: Blog NumberOfPosts: 'Number of Posts' + PLURALNAME: 'Blog Recent Posts Widgets' + PLURALS: + one: 'A Blog Recent Posts Widget' + other: '{count} Blog Recent Posts Widgets' + SINGULARNAME: 'Blog Recent Posts Widget' SilverStripe\Blog\Widgets\BlogTagsCloudWidget: Blog: Blog + PLURALNAME: 'Blog Tags Cloud Widgets' + PLURALS: + one: 'A Blog Tags Cloud Widget' + other: '{count} Blog Tags Cloud Widgets' + SINGULARNAME: 'Blog Tags Cloud Widget' SilverStripe\Blog\Widgets\BlogTagsWidget: Blog: Blog Direction: Direction Direction_Description: 'Change the direction of ordering of tags shown by this widget.' Limit: Limit Limit_Description: 'Limit the number of tags shown by this widget (set to 0 to show all tags).' + PLURALNAME: 'Blog Tags Widgets' + PLURALS: + one: 'A Blog Tags Widget' + other: '{count} Blog Tags Widgets' + SINGULARNAME: 'Blog Tags Widget' Sort: Sort Sort_Description: 'Change the order of tags shown by this widget.' diff --git a/license.md b/license.md index 759b61f..f717a37 100644 --- a/license.md +++ b/license.md @@ -1,4 +1,4 @@ -Copyright (c) 2017, Michael Strong +Copyright (c) 2018, Michael Strong All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/package.json b/package.json index 4fe994b..6ca65e0 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,30 @@ { "name": "silverstripe-blog", "version": "3.0.0", - "description": "Silverstripe blog module", - "main": "index.js", - "directories": { - "doc": "docs", - "test": "tests" - }, + "description": "A fresh take on blogging in Silverstripe set out to tackle the issue of a cluttered Site Tree", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "yarn && NODE_ENV=production webpack -p --bail --progress", + "watch": "yarn && NODE_ENV=development webpack --watch --progress", + "css": "WEBPACK_CHILD=css npm run build", + "lint": "eslint client/src; sass-lint -v" }, "repository": { "type": "git", "url": "git+https://github.com/silverstripe/silverstripe-blog.git" }, - "author": "", - "license": "BSD-3", + "keywords": [ + "silverstripe", + "blog" + ], + "author": "SilverStripe Ltd", + "license": "BSD-2-Clause", "bugs": { "url": "https://github.com/silverstripe/silverstripe-blog/issues" }, "homepage": "https://github.com/silverstripe/silverstripe-blog#readme", + "dependencies": [], "devDependencies": { - "gulp": "^3.9.1", - "gulp-sass": "^2.3.1", - "gulp-watch": "^4.3.6" + "@silverstripe/eslint-config": "^0.0.4", + "@silverstripe/webpack-config": "^0.5" } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index c4c9ca1..f588513 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -7,16 +7,6 @@ - - - - - - - - - - diff --git a/scss/blog.scss b/scss/blog.scss deleted file mode 100644 index 2f022cf..0000000 --- a/scss/blog.scss +++ /dev/null @@ -1,75 +0,0 @@ -.no-sidebar .content-container.size3of4 { - width: 75%; -} - -.blog-entry .post-image img { - width: 98.75%; -} - -.blog-sidebar .WidgetHolder ul { - margin-left: 0; - - li { - list-style-type: none; - } -} - -// tag cloud related -$baseTagFontSize:4pt; - -ul.blogTagCloud { - list-style-type: none; - clear: both; - - li { - float: left; - display: inline; - padding-right: 8px; - - a span { - float: left; - line-height: 30px; text-align: center; - padding: 0px; - } - } - - .tagCount10 { - font-size: $baseTagFontSize+22pt; - } - - .tagCount9 { - font-size: $baseTagFontSize+20pt; - } - - .tagCount8 { - font-size: $baseTagFontSize+18pt; - } - - .tagCount7 { - font-size: $baseTagFontSize+16pt; - } - - .tagCount6 { - font-size: $baseTagFontSize+14pt; - } - - .tagCount5 { - font-size: $baseTagFontSize+12pt; - } - - .tagCount4 { - font-size: $baseTagFontSize+10pt; - } - - .tagCount3 { - font-size: $baseTagFontSize+8pt; - } - - .tagCount2 { - font-size: $baseTagFontSize+6pt; - } - - .tagCount1 { - font-size: $baseTagFontSize+4pt; - } -} diff --git a/scss/categories-tags.scss b/scss/categories-tags.scss deleted file mode 100644 index e69de29..0000000 diff --git a/scss/cms.scss b/scss/cms.scss deleted file mode 100755 index 478c037..0000000 --- a/scss/cms.scss +++ /dev/null @@ -1,206 +0,0 @@ -/** - * CMS Styles - */ -/** - * Include Compass framework - */ -// @import "compass"; -/* - * Sprite maps & Icons - */ -// @import "compass/utilities/sprites/base"; -// @import "blog-icon/*.png"; -// buttons - -#FeaturedImage .middleColumn { - clear: none; - float: left; -} - -.has-panel .cms-content-tools.blog-admin-sidebar { - width: 280px; - border-right: none; - border-left: 1px solid #C0C0C2; - position: absolute !important; /* overrides cms !imporant style */ - right: 0px; - top: 0; - height: 100%; - - .cms-panel-toggle a { - text-align: left; - margin: 0; - } - - .cms-panel-toggle.south { - border-top: 1px solid #aaaaaa; - } - - ~ .blog-admin-outer { - width: 100%; - padding-right: 280px; - position: absolute; - height: 100%; - overflow-y: hidden; - overflow-x: hidden; - box-sizing: border-box; - - > .ss-tabset { - position: relative; - overflow: auto; - height: 100%; - width: 100%; - - #Title { - label { - float: none; - } - .middleColumn, input { - width: 100%; - max-width: 100%; - margin-left: 0; - } - } - } - } - - .cms-content-view { - > .field { - + .field { - margin-top: 10px; - } - - &.urlsegment { - .preview { - padding-top: 0; - line-height: 25px; - } - - .edit { - float: right; - } - } - - &.datetime { - > .middleColumn { - > .date { - width: 60%; - } - - > .time { - width: 36%; - float: right; - } - - .middleColumn, input { - width: 100%; - } - } - } - } - } - - &.collapsed { - ~ .blog-admin-outer { - padding-right: 41px; - - #Root_Main { - margin-right: 15px; - } - } - - } - - &.cms-content-tools { - .cms-panel-content { - width: auto; - } - } -} - -.toggle-description { - text-indent: -1000000px; - display: inline-block; - background: url("../images/information.png") no-repeat center center; - width: 20px; - height: 20px; - margin-left: 4px; - cursor:pointer; -} - -.middleColumn.toggle-description-correct-middle { - margin-left: 0; - float: left; - width: 416px; -} - -.tab-content .field p.toggle-description-correct-right { - display: inline-block; - margin-left: 0; - padding-left: 0; - clear: none; - float: left; -} - -.description.toggle-description-correct-description { - width: 416px; - padding: 12px 0; -} - -.custom-summary { - .ui-accordion-content .field { - margin: 0; - } - - .ui-accordion-content, - .ui-accordion-content .field { - padding: 0; - } - - .ui-icon-triangle-1-e { - background-position: -16px -128px; - } -} - -.cms table.ss-gridfield-table { - tr td.MergeAction { - width: 225px; - - a { - display: block; - height: 100%; - width: 100%; - } - - select { - width: 150px; - } - } -} - -.cms-content-actions, -.cms-preview-controls { - z-index: 999; -} - -.blog-cms-categorisation { - .MergeActionReveal { - margin-left: 10px; - } - - .toolbar--content { - margin-top: 0; - } - - .MergeActionReveal:after { - content: ''; - background: url('../images/move-icon.png'); - display: inline-block; - height: 16px; - width: 16px; - margin-left: 4px; - } - - button.action { - margin-left: 5px; - } -} diff --git a/src/Admin/GridFieldCategorisationConfig.php b/src/Admin/GridFieldCategorisationConfig.php index eac622e..15a2019 100644 --- a/src/Admin/GridFieldCategorisationConfig.php +++ b/src/Admin/GridFieldCategorisationConfig.php @@ -3,9 +3,9 @@ namespace SilverStripe\Blog\Admin; use SilverStripe\Blog\Forms\GridField\GridFieldAddByDBField; -use SilverStripe\Blog\Admin\GridFieldMergeAction; use SilverStripe\Blog\Model\CategorisationObject; use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; +use SilverStripe\Forms\GridField\GridFieldDataColumns; class GridFieldCategorisationConfig extends GridFieldConfig_RecordEditor { @@ -16,7 +16,7 @@ class GridFieldCategorisationConfig extends GridFieldConfig_RecordEditor * @param string $parentMethod * @param string $childMethod */ - public function __construct($itemsPerPage = 15, $mergeRecords, $parentType, $parentMethod, $childMethod) + public function __construct($itemsPerPage, $mergeRecords, $parentType, $parentMethod, $childMethod) { parent::__construct($itemsPerPage); diff --git a/src/Admin/GridFieldMergeAction.php b/src/Admin/GridFieldMergeAction.php index d2c5fd5..272f8d0 100644 --- a/src/Admin/GridFieldMergeAction.php +++ b/src/Admin/GridFieldMergeAction.php @@ -2,13 +2,13 @@ namespace SilverStripe\Blog\Admin; -use SilverStripe\Blog\Admin\GridFieldFormAction; use SilverStripe\Control\Controller; use SilverStripe\Core\Injector\Injectable; use Silverstripe\Forms\DropdownField; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField_ActionProvider; use SilverStripe\Forms\GridField\GridField_ColumnProvider; +use SilverStripe\ORM\SS_List; class GridFieldMergeAction implements GridField_ColumnProvider, GridField_ActionProvider { @@ -48,7 +48,7 @@ class GridFieldMergeAction implements GridField_ColumnProvider, GridField_Action * @param string $parentMethod * @param string $childMethod */ - public function __construct($records = [], $parentType, $parentMethod, $childMethod) + public function __construct($records, $parentType, $parentMethod, $childMethod) { $this->records = $records; $this->parentType = $parentType; @@ -101,7 +101,10 @@ class GridFieldMergeAction implements GridField_ColumnProvider, GridField_Action 'data-target' => $prefix . '-target-record-' . $record->ID ]); - return $dropdown->Field() . $action->Field() . 'move posts to'; + $action->addExtraClass('btn btn-primary btn-sm blog-merge-action'); + + return $dropdown->Field() . $action->Field() . + 'Move posts to'; } return null; diff --git a/src/Forms/GridField/GridFieldAddByDBField.php b/src/Forms/GridField/GridFieldAddByDBField.php index 50781a0..d22ff58 100644 --- a/src/Forms/GridField/GridFieldAddByDBField.php +++ b/src/Forms/GridField/GridFieldAddByDBField.php @@ -2,11 +2,9 @@ namespace SilverStripe\Blog\Forms\GridField; -use SilverStripe\Core\Injector\Injectable; -use SilverStripe\Core\Manifest\ModuleLoader; -use UnexpectedValueException; use SilverStripe\Control\Controller; use SilverStripe\Core\Convert; +use SilverStripe\Core\Injector\Injectable; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField_ActionProvider; use SilverStripe\Forms\GridField\GridField_FormAction; @@ -16,6 +14,7 @@ use SilverStripe\ORM\ArrayList; use SilverStripe\Security\Security; use SilverStripe\View\ArrayData; use SilverStripe\View\Requirements; +use UnexpectedValueException; class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLProvider { @@ -159,7 +158,7 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP */ public function getHTMLFragments($gridField) { - Requirements::javascript(ModuleLoader::getModule('silverstripe/blog')->getRelativeResourcePath('js/gridfieldaddbydbfield.js')); + Requirements::javascript('silverstripe/blog:client/dist/js/main.bundle.js'); /** * @var DataList $dataList diff --git a/src/Forms/GridField/GridFieldBlogPostState.php b/src/Forms/GridField/GridFieldBlogPostState.php index 4b5a4de..b73d782 100644 --- a/src/Forms/GridField/GridFieldBlogPostState.php +++ b/src/Forms/GridField/GridFieldBlogPostState.php @@ -3,10 +3,8 @@ namespace SilverStripe\Blog\Forms\GridField; use SilverStripe\Blog\Model\BlogPost; -use SilverStripe\Core\Manifest\ModuleLoader; use SilverStripe\Lumberjack\Forms\GridFieldSiteTreeState; use SilverStripe\ORM\FieldType\DBDatetime; -use SilverStripe\View\Requirements; /** * Provides a component to the {@link GridField} which tells the user whether or not a blog post @@ -21,7 +19,6 @@ class GridFieldBlogPostState extends GridFieldSiteTreeState public function getColumnContent($gridField, $record, $columnName) { if ($columnName == 'State') { - Requirements::css('silverstripe/blog:css/cms.css'); if ($record instanceof BlogPost) { $modifiedLabel = ''; diff --git a/src/Forms/GridField/GridFieldConfig_BlogPost.php b/src/Forms/GridField/GridFieldConfigBlogPost.php similarity index 90% rename from src/Forms/GridField/GridFieldConfig_BlogPost.php rename to src/Forms/GridField/GridFieldConfigBlogPost.php index e14d948..5be03f3 100644 --- a/src/Forms/GridField/GridFieldConfig_BlogPost.php +++ b/src/Forms/GridField/GridFieldConfigBlogPost.php @@ -10,7 +10,7 @@ use SilverStripe\Lumberjack\Forms\GridFieldSiteTreeState; * GridField config necessary for managing a SiteTree object. * */ -class GridFieldConfig_BlogPost extends GridFieldConfig_Lumberjack +class GridFieldConfigBlogPost extends GridFieldConfig_Lumberjack { /** * @param null|int $itemsPerPage diff --git a/src/Model/Blog.php b/src/Model/Blog.php index 565ce41..6276dbf 100644 --- a/src/Model/Blog.php +++ b/src/Model/Blog.php @@ -4,11 +4,10 @@ namespace SilverStripe\Blog\Model; use Page; use SilverStripe\Blog\Admin\GridFieldCategorisationConfig; -use SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost; +use SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost; use SilverStripe\CMS\Controllers\RootURLController; use SilverStripe\Control\Controller; use SilverStripe\Core\Convert; -use SilverStripe\Core\Manifest\ModuleLoader; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\ListboxField; use SilverStripe\Forms\LiteralField; @@ -127,7 +126,7 @@ class Blog extends Page implements PermissionProvider */ private static $description = 'Adds a blog to your website.'; - private static $icon = 'blog/images/site-tree-icon.png'; + private static $icon = 'silverstripe/blog:client/images/site-tree-icon.png'; /** * {@inheritdoc} @@ -189,9 +188,8 @@ class Blog extends Page implements PermissionProvider */ protected function addCMSRequirements() { - $module = ModuleLoader::getModule('silverstripe/blog'); - Requirements::css($module->getRelativeResourcePath('css/cms.css')); - Requirements::javascript($module->getRelativeResourcePath('js/cms.js')); + Requirements::css('silverstripe/blog:client/dist/styles/main.css'); + Requirements::javascript('silverstripe/blog:client/dist/js/main.bundle.js'); } /** * {@inheritdoc} @@ -355,7 +353,10 @@ class Blog extends Page implements PermissionProvider ); $members = $this->getCandidateUsers()->map()->toArray(); - $toggleButton = LiteralField::create('ToggleButton', ''); + $toggleButton = LiteralField::create( + 'ToggleButton', + '' + ); $editorField = ListboxField::create('Editors', 'Editors', $members) ->setRightTitle($toggleButton) @@ -609,7 +610,7 @@ class Blog extends Page implements PermissionProvider */ public function getLumberjackGridFieldConfig() { - return GridFieldConfig_BlogPost::create(); + return GridFieldConfigBlogPost::create(); } /** diff --git a/src/Model/BlogCategory.php b/src/Model/BlogCategory.php index ead746a..ae69c7d 100644 --- a/src/Model/BlogCategory.php +++ b/src/Model/BlogCategory.php @@ -2,10 +2,6 @@ namespace SilverStripe\Blog\Model; -use SilverStripe\Blog\Model\Blog; -use SilverStripe\Blog\Model\BlogObject; -use SilverStripe\Blog\Model\BlogPost; -use SilverStripe\Blog\Model\CategorisationObject; use SilverStripe\ORM\DataObject; /** diff --git a/src/Model/BlogCommentExtension.php b/src/Model/BlogCommentExtension.php index 9768a60..8bda704 100644 --- a/src/Model/BlogCommentExtension.php +++ b/src/Model/BlogCommentExtension.php @@ -2,7 +2,6 @@ namespace SilverStripe\Blog\Model; -use SilverStripe\Blog\Model\BlogPost; use SilverStripe\ORM\DataExtension; /** diff --git a/src/Model/BlogFilter.php b/src/Model/BlogFilter.php index 02238f1..3b91b06 100644 --- a/src/Model/BlogFilter.php +++ b/src/Model/BlogFilter.php @@ -2,9 +2,7 @@ namespace SilverStripe\Blog\Model; -use SilverStripe\Blog\Model\Blog; use SilverStripe\Blog\Model\BlogFilter\BlogFilterGridField; -use SilverStripe\Blog\Model\BlogPost; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Convert; use SilverStripe\Forms\FieldList; @@ -12,8 +10,8 @@ use SilverStripe\Forms\Tab; use SilverStripe\Lumberjack\Model\Lumberjack; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBDatetime; -use SilverStripe\Versioned\Versioned; use SilverStripe\Security\Permission; +use SilverStripe\Versioned\Versioned; /** * This class is responsible for filtering the SiteTree when necessary and also overlaps into diff --git a/src/Model/BlogMemberExtension.php b/src/Model/BlogMemberExtension.php index 45da9e7..73eadd2 100644 --- a/src/Model/BlogMemberExtension.php +++ b/src/Model/BlogMemberExtension.php @@ -3,9 +3,7 @@ namespace SilverStripe\Blog\Model; use SilverStripe\Assets\Image; -use SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost; -use SilverStripe\Blog\Model\BlogPost; -use SilverStripe\Core\Manifest\ModuleLoader; +use SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\Tab; @@ -113,9 +111,8 @@ class BlogMemberExtension extends DataExtension $fields->removeFieldFromTab('Root', 'BlogPosts'); // Construct a better posts tab. - $module = ModuleLoader::getModule('silverstripe/blog'); - Requirements::css($module->getRelativeResourcePath('css/cms.css')); - Requirements::javascript($module->getRelativeResourcePath('js/cms.js')); + Requirements::css('silverstripe/blog:client/dist/styles/main.css'); + Requirements::javascript('silverstripe/blog:client/dist/js/main.bundle.js'); $tab = Tab::create('BlogPosts', _t(__CLASS__ . '.TABBLOGPOSTS', 'Blog Posts')); @@ -123,7 +120,7 @@ class BlogMemberExtension extends DataExtension 'BlogPosts', _t(__CLASS__ . '.BLOGPOSTS', 'Blog Posts'), $this->owner->BlogPosts(), - GridFieldConfig_BlogPost::create() + GridFieldConfigBlogPost::create() ); $tab->Fields()->add($gridField); diff --git a/src/Model/BlogObject.php b/src/Model/BlogObject.php index db5fc73..dfc17ec 100644 --- a/src/Model/BlogObject.php +++ b/src/Model/BlogObject.php @@ -3,7 +3,6 @@ namespace SilverStripe\Blog\Model; use SilverStripe\Control\Controller; -use SilverStripe\Core\ClassInfo; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Tab; use SilverStripe\Forms\TabSet; diff --git a/src/Model/BlogPost.php b/src/Model/BlogPost.php index cb13057..505b6df 100644 --- a/src/Model/BlogPost.php +++ b/src/Model/BlogPost.php @@ -3,21 +3,15 @@ namespace SilverStripe\Blog\Model; use Page; +use SilverStripe\AssetAdmin\Forms\UploadField; use SilverStripe\Assets\Image; -use SilverStripe\Blog\Forms\BlogAdminSidebar; -use SilverStripe\Blog\Model\BlogCategory; -use SilverStripe\Blog\Model\BlogPostFilter; -use SilverStripe\Blog\Model\BlogTag; use SilverStripe\Control\Controller; use SilverStripe\Core\Config\Config; -use SilverStripe\Core\Manifest\ModuleLoader; use SilverStripe\Forms\DatetimeField; -use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\HTMLEditor\HTMLEditorField; use SilverStripe\Forms\ListboxField; use SilverStripe\Forms\TextField; use SilverStripe\Forms\ToggleCompositeField; -use SilverStripe\AssetAdmin\Forms\UploadField; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\ORM\UnsavedRelationList; @@ -211,9 +205,8 @@ class BlogPost extends Page */ public function getCMSFields() { - $module = ModuleLoader::getModule('silverstripe/blog'); - Requirements::css($module->getRelativeResourcePath('css/cms.css')); - Requirements::javascript($module->getRelativeResourcePath('js/cms.js')); + Requirements::css('silverstripe/blog:client/dist/styles/main.css'); + Requirements::javascript('silverstripe/blog:client/dist/js/main.bundle.js'); $this->beforeUpdateCMSFields(function ($fields) { $uploadField = UploadField::create('FeaturedImage', _t(__CLASS__ . '.FeaturedImage', 'Featured Image')); @@ -265,7 +258,8 @@ class BlogPost extends Page )->setDescription( _t( __CLASS__ . '.AdditionalCredits_Description', - 'If some authors of this post don\'t have CMS access, enter their name(s) here. You can separate multiple names with a comma.' + 'If some authors of this post don\'t have CMS access, enter their name(s) here. '. + 'You can separate multiple names with a comma.' ) ); diff --git a/src/Model/BlogPostFilter.php b/src/Model/BlogPostFilter.php index 240a3f9..3900715 100644 --- a/src/Model/BlogPostFilter.php +++ b/src/Model/BlogPostFilter.php @@ -10,8 +10,8 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataQuery; use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\ORM\Queries\SQLSelect; -use SilverStripe\Versioned\Versioned; use SilverStripe\Security\Permission; +use SilverStripe\Versioned\Versioned; /** * This is responsible for filtering only published posts to users who do not have permission to diff --git a/src/Model/BlogTag.php b/src/Model/BlogTag.php index bc6b351..4180f67 100644 --- a/src/Model/BlogTag.php +++ b/src/Model/BlogTag.php @@ -3,10 +3,6 @@ namespace SilverStripe\Blog\Model; use SilverStripe\ORM\DataObject; -use SilverStripe\Blog\Model\Blog; -use SilverStripe\Blog\Model\BlogObject; -use SilverStripe\Blog\Model\BlogPost; -use SilverStripe\Blog\Model\CategorisationObject; /** * A blog tag for keyword descriptions of a blog post. diff --git a/src/Widgets/BlogRecentPostsWidget.php b/src/Widgets/BlogRecentPostsWidget.php index 9a3a993..b53dd92 100644 --- a/src/Widgets/BlogRecentPostsWidget.php +++ b/src/Widgets/BlogRecentPostsWidget.php @@ -8,6 +8,7 @@ if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) { use SilverStripe\Blog\Model\Blog; use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FieldList; use SilverStripe\Forms\NumericField; use SilverStripe\Widgets\Model\Widget; diff --git a/templates/SilverStripe/Blog/Model/Layout/Blog.ss b/templates/SilverStripe/Blog/Model/Layout/Blog.ss index c2b6d25..8d04d49 100644 --- a/templates/SilverStripe/Blog/Model/Layout/Blog.ss +++ b/templates/SilverStripe/Blog/Model/Layout/Blog.ss @@ -1,4 +1,4 @@ -<% require themedCSS('blog', 'blog') %> +<% require css('silverstripe/blog: client/dist/styles/main.css') %>
diff --git a/templates/SilverStripe/Blog/Model/Layout/BlogPost.ss b/templates/SilverStripe/Blog/Model/Layout/BlogPost.ss index 90466c0..8b886a5 100644 --- a/templates/SilverStripe/Blog/Model/Layout/BlogPost.ss +++ b/templates/SilverStripe/Blog/Model/Layout/BlogPost.ss @@ -1,4 +1,4 @@ -<% require themedCSS('blog', 'blog') %> +<% require css('silverstripe/blog: client/dist/styles/main.css') %>
diff --git a/templates/SilverStripe/Blog/Model/Layout/Blog_profile.ss b/templates/SilverStripe/Blog/Model/Layout/Blog_profile.ss index 36eedf2..825128e 100644 --- a/templates/SilverStripe/Blog/Model/Layout/Blog_profile.ss +++ b/templates/SilverStripe/Blog/Model/Layout/Blog_profile.ss @@ -1,4 +1,4 @@ -<% require themedCSS('blog', 'blog') %> +<% require css('silverstripe/blog: client/dist/styles/main.css') %>
diff --git a/tests/BlogTest.php b/tests/BlogTest.php index 553597b..364062c 100755 --- a/tests/BlogTest.php +++ b/tests/BlogTest.php @@ -360,7 +360,11 @@ class BlogTest extends SapphireTest $this->fail('The "profile" action should throw a HTTPResponse_Exception when disable_profiles is enabled'); } catch (HTTPResponse_Exception $e) { - $this->assertEquals(404, $e->getResponse()->getStatusCode(), 'The response status code should be 404 Not Found'); + $this->assertEquals( + 404, + $e->getResponse()->getStatusCode(), + 'The response status code should be 404 Not Found' + ); } } diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..45b855a --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,55 @@ +const Path = require('path'); +const webpack = require('webpack'); +// Import the core config +const webpackConfig = require('@silverstripe/webpack-config'); +const { + resolveJS, + externalJS, + moduleJS, + pluginJS, + moduleCSS, + pluginCSS, +} = webpackConfig; + +const ENV = process.env.NODE_ENV; +const PATHS = { + ROOT: Path.resolve(), + MODULES: 'node_modules', + FILES_PATH: '../', + THIRDPARTY: 'thirdparty', + SRC: Path.resolve('client/src'), + DIST: Path.resolve('client/dist'), +}; + +const config = [ + { + name: 'bundle', + entry: { + main: `${PATHS.SRC}/main.js` + }, + output: { + path: PATHS.DIST, + filename: 'js/[name].bundle.js', + }, + devtool: (ENV !== 'production') ? 'source-map' : '', + resolve: resolveJS(ENV, PATHS), + externals: externalJS(ENV, PATHS), + module: moduleJS(ENV, PATHS), + plugins: pluginJS(ENV, PATHS), + }, + { + name: 'bundle', + entry: { + main: `${PATHS.SRC}/main.scss` + }, + output: { + path: PATHS.DIST, + filename: 'styles/[name].css' + }, + devtool: (ENV !== 'production') ? 'source-map' : '', + module: moduleCSS(ENV, PATHS), + plugins: pluginCSS(ENV, PATHS), + }, +]; + +module.exports = config;