Initial theming support

This commit is contained in:
Tony Air 2019-04-11 05:15:29 +07:00
parent 003d282905
commit 8429a6739a
45 changed files with 832 additions and 177 deletions

View File

@ -20,5 +20,12 @@ $config->enablePlugins([
$config->addButtonsToLine(2, 'hr');
$config->setOption('block_formats', 'Paragraph=p;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Address=address;Pre=pre');
$config->setOption('invalid_elements', 'h1');
$config->setOption(
'table_class_list',
[
['title' => 'Transparent Table', 'value' => 'table-none'],
['title' => 'Shaded rows', 'value' => 'table table-striped table-bordered'],
]
);
FulltextSearchable::enable();

View File

@ -19,8 +19,8 @@ SilverStripe\View\SSViewer:
SilverStripe\Admin\LeftAndMain:
extra_requirements_css:
- 'app/client/dist/css/cms.css'
- 'app/client/dist/css/app_cms.css'
SilverStripe\Forms\HTMLEditor\TinyMCEConfig:
editor_css:
- 'app/client/dist/css/editor.css'
- 'app/client/dist/css/app_editor.css'

View File

@ -25,6 +25,7 @@ SilverStripe\CMS\Model\SiteTree:
- Dynamic\Elements\Oembed\Elements\ElementOembed
- Dynamic\Elements\Elements\ElementTestimonials
- Site\Elements\TeamMembersElement
- Site\Elements\SliderElement
DNADesign\ElementalList\Model\ElementList:
default_global_elements: false
@ -50,4 +51,3 @@ DNADesign\Elemental\Models\ElementContent:
Dynamic\Elements\Image\Elements\ElementImage:
extensions:
- Site\Extensions\ElementImageWidget

View File

@ -1,6 +1,5 @@
SilverStripe\SiteConfig\SiteConfig:
extensions:
- Broarm\OpeningHours\OpeningHours
- Site\Extensions\SiteConfigExtension
- Site\Extensions\SocialExtension
@ -19,3 +18,18 @@ Dynamic\FlexSlider\Model\SlideImage:
SilverStripe\Core\Injector\Injector:
SilverStripe\UserForms\Model\UserDefinedForm:
class: Site\Extensions\CMSMain_HiddenClass
SilverStripe\Security\MemberAuthenticator\LostPasswordHandler:
class: Site\Extensions\LostPasswordHandlerExtension
# User Forms
SilverStripe\UserForms\Form\UserForm:
extensions:
- Site\Extensions\PlaceholderFormExtension
SilverStripe\UserForms\Model\UserDefinedForm:
extensions:
- Site\Extensions\UserDefinedFormExtension
DNADesign\ElementalUserForms\Model\ElementForm:
extensions:
- Site\Extensions\UserDefinedFormExtension

79
app/_config/files.yml Normal file
View File

@ -0,0 +1,79 @@
---
Name: webapp-files
---
SilverStripe\Assets\Upload_Validator:
allowedExtensions:
- 'stl'
SilverStripe\Assets\File:
allowed_extensions:
- 'ace'
- 'arc'
- 'arj'
- 'asf'
- 'au'
- 'avi'
- 'bmp'
- 'bz2'
- 'cab'
- 'cda'
- 'csv'
- 'dmg'
- 'doc'
- 'docx'
- 'dotx'
- 'flv'
- 'gif'
- 'gpx'
- 'gz'
- 'hqx'
- 'ico'
- 'jpeg'
- 'jpg'
- 'kml'
- 'm4a'
- 'm4v'
- 'mid'
- 'midi'
- 'mkv'
- 'mov'
- 'mp3'
- 'mp4'
- 'mpa'
- 'mpeg'
- 'mpg'
- 'ogg'
- 'ogv'
- 'pages'
- 'pcx'
- 'pdf'
- 'png'
- 'pps'
- 'ppt'
- 'pptx'
- 'potx'
- 'ra'
- 'ram'
- 'rm'
- 'rtf'
- 'sit'
- 'sitx'
- 'tar'
- 'tgz'
- 'tif'
- 'tiff'
- 'txt'
- 'wav'
- 'webm'
- 'wma'
- 'wmv'
- 'xls'
- 'xlsx'
- 'xltx'
- 'zip'
- 'zipx'
- 'stl'
app_categories:
document:
- 'stl'

View File

@ -3,9 +3,11 @@
# Cuz WebPack compiling script use it to set configuration
Site\Templates\WebpackTemplateProvider:
SRC: app/client/src
DIST: app/client/dist
APPDIR: app
THEMESDIR: themes
HOSTNAME: localhost
PORT: 3000
TYPESJS: app/client/src/js/types
TYPESSCSS: app/client/src/scss/types
SRC: client/src
DIST: client/dist
TYPESJS: client/src/js/types
TYPESSCSS: client/src/scss/types

View File

@ -43,8 +43,7 @@ const CarouselUI = (($) => {
// create arrows
if ($e.data('arrows')) {
let $prev = $('<i class="carousel-control-prev" data-target="#' + id + '" role="button" data-slide="prev"><i class="fas fa-chevron-left" aria-hidden="true"></i><i class="sr-only">Previous</i></i>');
$e.prepend($prev);
$e.prepend('<i class="carousel-control-prev" data-target="#' + id + '" role="button" data-slide="prev"><i class="fas fa-chevron-left" aria-hidden="true"></i><i class="sr-only">Previous</i></i>');
$e.prepend('<i class="carousel-control-next" data-target="#' + id + '" role="button" data-slide="next"><i class="fas fa-chevron-right" aria-hidden="true"></i><i class="sr-only">Next</i></i>');
}
@ -68,8 +67,8 @@ const CarouselUI = (($) => {
$(event.target).carousel('prev');
});
$e.find('img').hammer().bind('tap', (event) => {
$e.carousel('next');
$e.hammer().bind('tap', (event) => {
$(event.target).carousel('next');
});
});
}
@ -88,4 +87,4 @@ const CarouselUI = (($) => {
return CarouselUI;
})($);
export default CarouselUI;
export default CarouselUI;

View File

@ -27,10 +27,26 @@ const FormBasics = (($) => {
const $el = $(el);
$el.selectpicker({
liveSearch: $el.data('live-search')
iconBase: 'fas',
tickIcon: 'fa-check',
liveSearch: $el.data('live-search'),
noneSelectedText: $el.data('none-selected-text') || 'Nothing selected',
maxOptions: $el.data('max-options') || false,
});
// FIX: hidden picker
$el.parents('.field.dropdown').find('.dropdown-toggle').click();
$el.parents('.field.dropdown').find('.dropdown-toggle').click();
$el.parents('.field.dropdown').find('.dropdown-toggle').blur();
});
// FIX: hidden picker click
if ($selectFields.length) {
setTimeout(() => {
$(window).scrollTop(0, 0);
}, 600);
}
$fields.each((e, el) => {
const $el = $(el);

View File

@ -104,6 +104,10 @@ const CroppieUI = (($) => {
data.append(name, blob, name + '-image.png');
data.append('ajax', '1');
if (!$(form).data('jsFormValidate').validate()) {
return false;
}
$.ajax({
url: $(form).attr('action'),
data: data,
@ -112,27 +116,29 @@ const CroppieUI = (($) => {
type: $(form).attr('method'),
success: function(data) {
let IS_JSON = false;
let json = {};
try {
IS_JSON = true;
const json = $.parseJSON(data);
json = $.parseJSON(data);
} catch (e) {
IS_JSON = false;
}
if (IS_JSON && typeof json === 'object') {
for (let k in json) {
if (IS_JSON) {
/*for (let k in json) {
$form.find('select[name="' + k + '"],input[name="' + k + '"],textarea[name="' + k + '"]').setError(true, json[k]);
}
}*/
if (typeof json['status'] !== 'undefined' && json['status'] === 'success') {
if (typeof json['link'] !== 'undefined') {
G.location = json['link'];
} else {
G.location.reload(false);
//G.location.reload(false);
}
}
} else {
G.location.reload(false);
$(form).replaceWith(data);
//G.location.reload(false);
}
SpinnerUI.hide();

View File

@ -65,22 +65,26 @@ const FormValidateField = (($) => {
});
}
this.removeError();
if (valid) {
this.removeError();
return true;
}
this.setError(scrollTo, msg);
setTimeout(() => {
this.setError(scrollTo, msg);
}, 500);
return false;
}
valideURL(str) {
const regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
if (!regex.test(str)) {
return false;
} else {
return true;
}
const pattern = new RegExp('^(https?:\\/\\/){1}' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
return pattern.test(str);
}
setError(scrollTo = true, msg = null) {

View File

@ -1,6 +1,7 @@
import $ from 'jquery';
import Events from "../_events";
import FormValidateField from "./_ui.form.validate.field";
import SpinnerUI from './_ui.spinner';
const FormValidate = (($) => {
// Constants
@ -67,6 +68,7 @@ const FormValidate = (($) => {
validate(scrollTo = true, badCallback = false) {
console.log('Checking the form ...');
const ui = this;
let valid = true;
ui._fields.each(function(i, el) {
const $el = $(el);
@ -77,9 +79,14 @@ const FormValidate = (($) => {
badCallback();
}
console.log('Invalid form data');
SpinnerUI.hide();
valid = false;
return false;
}
});
return valid;
}
static _jQueryInterface() {

View File

@ -20,7 +20,7 @@ import FormDatetime from './_components/_ui.form.datetime';
import FormStepped from './_components/_ui.form.stepped';
import FormValidate from './_components/_ui.form.validate';
import FormStorage from './_components/_ui.form.storage';
import FormCroppie from './_components/_ui.form.croppie';
//import FormCroppie from './_components/_ui.form.croppie';
import AjaxUI from './_components/_ui.ajax';

View File

@ -15,7 +15,13 @@
}
.carousel-indicators li {
box-shadow: 1px 1px #000;
box-shadow: none;
// 1px 1px #000;
}
.carousel-title {
color: #fff;
}
.carousel-title,

View File

@ -0,0 +1,39 @@
// date-time fields
input.date,
input.time {
&[readonly] {
background-color: $white;
}
}
.bootstrap-timepicker-widget,
.datepicker-dropdown {
border: 1px solid #ced4da;
box-shadow: 0 0 3px #999;
}
.bootstrap-timepicker-widget {
.glyphicon {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
font-family: Font Awesome\ 5 Free;
font-weight: 900;
}
.glyphicon-chevron-up:before {
content: "\f077";
}
.glyphicon-chevron-down:before {
content: "\f078";
}
input {
border: 1px solid #ced4da;
}
}

View File

@ -60,6 +60,55 @@ button, input, optgroup, select, textarea,
.field {
margin: ($grid-gutter-height / 4) 0;
&.composite {
margin-top: 0;
margin-bottom: 0;
}
&.required {
&:after {
display: block;
position: absolute;
top: 2rem;
right: .5rem;
content: "*";
color: $red;
z-index: 2;
}
}
&.holder-error,
&.error {
input, select, textarea {
border-color: $red;
}
label {
color: $red;
}
}
.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) {
width: 100%;
}
}
.message {
@extend .alert;
@extend .alert-info;
display: block;
margin: .5rem 0;
}
.message.validation,
.message.required,
.message.error {
@extend .alert;
@extend .alert-danger;
}
// stick navbar to top using mobile layout

View File

@ -53,42 +53,12 @@ body.shrink {}
@extend .alert-danger;
}
// date-time fields
input.date,
input.time {
&[readonly] {
background-color: $white;
}
}
.bootstrap-timepicker-widget,
.datepicker-dropdown {
border: 1px solid #ced4da;
box-shadow: 0 0 3px #999;
}
.bootstrap-timepicker-widget {
.glyphicon {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
font-family: Font Awesome\ 5 Free;
font-weight: 900;
}
.glyphicon-chevron-up:before {
content: "\f077";
}
.glyphicon-chevron-down:before {
content: "\f078";
}
input {
border: 1px solid #ced4da;
.element {
&.site__elements__sliderelement {
.element-container {
width: 100%;
padding: 0;
max-width: none;
}
}
}

View File

@ -13,6 +13,7 @@
@import "_components/_ui.lightbox";
@import "_components/_ui.main";
@import "_components/_ui.form.basics";
@import "_components/_ui.elemental";
// Your custom styling

View File

@ -45,6 +45,15 @@ table {
@extend .table;
@extend .table-bordered;
@extend .table-striped;
}
table {
&.table-none {
border: 0;
tr, td, th {
border: 0;
background: none !important;
}
}
}

View File

@ -2,6 +2,10 @@ en:
SilverStripe\Security\Member:
SURNAME: 'Last Name'
db_Surname: 'Last Name'
SUBJECTPASSWORDRESET: 'Your password reset link'
ENTEREMAIL: 'Please enter an email address to get a password reset link.'
PASSWORDRESETSENTHEADER: 'Password reset link sent'
PASSWORDRESETSENTTEXT: 'Thank you. A reset link has been sent, provided an account exists for this email address.'
Page:
LOADINGTEXT: "LOADING .."
JAVASCRIPTREQUIRED: "Please, enable javascript!"
@ -13,3 +17,7 @@ en:
db_Surname: 'Last Name'
SilverShop\Page\CheckoutPage:
ProceedToPayment: 'Send Order to Store'
UndefinedOffset\NoCaptcha\Forms\NocaptchaField:
EMPTY: "Please prove you are human - check the Captcha box."
NOSCRIPT: "You must enable JavaScript to submit this form"
VALIDATE_ERROR: "Captcha could not be validated"

View File

@ -61,17 +61,19 @@ class SliderElement extends ElementSlideshow
]);
$grid = $fields->dataFieldByName('Slides');
$config = $grid->getConfig();
if ($grid) {
$config = $grid->getConfig();
$columns = new GridFieldEditableColumns();
$columns->setDisplayFields([
'Hide' => [
'title' => 'Hide it?',
'field' => CheckboxField::class,
],
]);
$columns = new GridFieldEditableColumns();
$columns->setDisplayFields([
'Hide' => [
'title' => 'Hide it?',
'field' => CheckboxField::class,
],
]);
$config->addComponent($columns);
$config->addComponent($columns);
}
return $fields;
}

View File

@ -0,0 +1,69 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:37 PM
*/
namespace Site\Extensions;
use Sheadawson\Linkable\Forms\LinkField;
use Sheadawson\Linkable\Models\Link;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\Security\MemberAuthenticator\LostPasswordHandler;
class LostPasswordHandlerExtension extends LostPasswordHandler
{
private static $url_handlers = [
'passwordsent' => 'passwordsent',
];
private static $allowed_actions = [
'passwordsent',
];
/**
* Show the "password sent" page, after a user has requested
* to reset their password.
*
* @return array
*/
public function passwordsent()
{
$message = _t(
'SilverStripe\\Security\\Security.PASSWORDRESETSENTTEXT',
"Thank you. A reset link has been sent, provided an account exists for this email address."
);
$email = $this->getRequest()->getVar('email');
$message = $email
? 'Thank you! A reset link has been sent to \''.$email.'\', provided an account exists for this email address.'
: $message;
return [
'Title' => _t(
'SilverStripe\\Security\\Security.PASSWORDRESETSENTHEADER',
"Password reset link sent".($email ? ' to \''.$email.'\'' : '')
),
'ElementalArea' => DBField::create_field('HTMLFragment', "<p>$message</p>"),
];
}
/**
* Avoid information disclosure by displaying the same status, regardless wether the email address actually exists
*
* @param array $data
* @return HTTPResponse
*/
protected function redirectToSuccess(array $data)
{
$link = $this->link('passwordsent').'?email='.$data['Email'];
return $this->redirect($this->addBackURLParam($link));
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Site\Extensions;
use SilverStripe\Core\Extension;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
class UserFormExtension extends Extension
{
public function updateFormFields(FieldList $fields)
{
foreach ($fields as $field) {
$this->setPlaceholder($field);
}
}
private function setPlaceholder($field)
{
if(is_a($field, TextField::class)) {
$field->setAttribute(
'placeholder',
$field->Title()
.($field->hasClass('requiredField') ? '*' : '')
);
$field->setTitle('');
}
if(is_a($field, CompositeField::class)) {
$children = $field->getChildren();
foreach ($children as $child) {
$this->setPlaceholder($child);
}
}
}
}

View File

@ -11,6 +11,7 @@ use SilverStripe\ORM\DataExtension;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TreeMultiselectField;
use SilverStripe\Forms\DropdownField;
use BetterBrief\GoogleMapField;
class SiteConfigExtension extends DataExtension

View File

@ -0,0 +1,79 @@
<?php
namespace Site\Extensions;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
use SilverStripe\Forms\GridField\GridFieldPaginator;
use SilverStripe\Forms\ListboxField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\UserForms\Model\EditableFormField;
class UserDefinedFormExtension extends DataExtension
{
private static $db = [
'CustomThankYouCode' => 'HTMLText',
'RedirectOnComplete' => 'Boolean(0)',
'RedirectOnCompleteURL' => 'Varchar(255)',
];
private static $many_many = [
'SubmissionColumns' => EditableFormField::class,
];
public function updateCMSFields(FieldList $fields)
{
parent::updateCMSFields($fields);
$fields->removeByName('RedirectOnComplete');
$fields->removeByName('RedirectOnCompleteURL');
$fields->removeByName('SubmissionColumns');
$fields->addFieldToTab(
'Root.Main',
ListboxField::create(
'SubmissionColumns',
'Display following columns at submissions list',
$this->owner->Fields()->map('ID', 'Title')
)
);
$tab = $fields->findOrMakeTab('Root.FormOptions');
/*$tab->push(CheckboxField::create('RedirectOnComplete'));
$tab->push(TextField::create('RedirectOnCompleteURL'));*/
$tab->push(TextareaField::create('CustomThankYouCode'));
$grid = $fields->dataFieldByName('Submissions');
$config = $grid->getConfig();
$config->getComponentByType(GridFieldPaginator::class)->setItemsPerPage(100);
$cols = $this->owner->SubmissionColumns();
if ($grid && $cols->count()) {
$dataCols = $config->getComponentByType(GridFieldDataColumns::class);
$columns = [
'ID' => 'ID',
'Created' => 'Created',
];
foreach ($cols as $col) {
$title = $col->getField('Title');
$name = $col->getField('Name');
$columns[$name] = [
'title' => $title,
'callback' => function($item) use ($name) {
return $item->relField($name);
}
];
}
$columns['Actions'] = 'Actions';
$dataCols->setDisplayFields($columns);
}
}
}

View File

@ -18,6 +18,8 @@ use SilverStripe\Forms\RequiredFields;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\ORM\ArrayList;
use DNADesign\Elemental\Models\ElementContent;
use DNADesign\Elemental\Models\ElementalArea;
use DNADesign\ElementalUserForms\Control\ElementFormController;
class PageController extends ContentController
{
@ -38,6 +40,26 @@ class PageController extends ContentController
return $this->render();
}
public function ElementalArea()
{
if($this->CurrentElement()) {
return false;
}
return ElementalArea::get()->byID($this->getField('ElementalAreaID'));
}
public function CurrentElement()
{
$contoller_curr = Controller::curr();
if(is_a($contoller_curr, ElementFormController::class)) {
return $contoller_curr;
}
return false;
}
public function SearchForm()
{
return Form::create(
@ -98,7 +120,7 @@ class PageController extends ContentController
$results->removeDuplicates();
return ArrayData::create([
'Title' => 'Search query "'.$term.'"',
'Title' => 'You searched for: "'.$term.'"',
'Results' => PaginatedList::create($results),
]);
}
@ -124,8 +146,10 @@ class PageController extends ContentController
public function getSiteWideMessage()
{
if (!$this->site_message) {
$session = $this->getRequest()->getSession();
$request = $this->getRequest();
if ($request->isGET() && !$this->site_message) {
$session = $request->getSession();
$this->site_message = $session->get('SiteWideMessage');
$session->clear('SiteWideMessage');
}

View File

@ -36,6 +36,7 @@ class DeferedRequirements implements TemplateGlobalProvider
public static function Auto($class = false)
{
$config = Config::inst()->get(self::class);
$themeName = WebpackTemplateProvider::themeName();
// Initialization
Requirements::block(THIRDPARTY_DIR.'/jquery/jquery.js');
@ -50,10 +51,10 @@ class DeferedRequirements implements TemplateGlobalProvider
}
// App libs
if (!$config['nofontawesome']) {
DeferedRequirements::loadCSS('//use.fontawesome.com/releases/v5.3.1/css/all.css');
DeferedRequirements::loadCSS('//use.fontawesome.com/releases/v5.4.0/css/all.css');
}
DeferedRequirements::loadCSS('app.css');
DeferedRequirements::loadJS('app.js');
DeferedRequirements::loadCSS($themeName.'.css');
DeferedRequirements::loadJS($themeName.'.js');
// Class libs
$class = get_class(Controller::curr());
@ -72,17 +73,17 @@ class DeferedRequirements implements TemplateGlobalProvider
$dir = Path::join(
Director::publicFolder(),
ManifestFileFinder::RESOURCES_DIR,
'app',
$themeName,
'client',
'dist'
);
if (file_exists(Path::join($dir, 'css', $class . '.css'))) {
DeferedRequirements::loadCSS($class . '.css');
if (file_exists(Path::join($dir, 'css', $themeName.'_'.$class . '.css'))) {
DeferedRequirements::loadCSS($themeName.'_'.$class . '.css');
}
if (file_exists(Path::join($dir, 'js', $class . '.js'))) {
DeferedRequirements::loadJS($class . '.js');
if (file_exists(Path::join($dir, 'js', $themeName.'_'.$class . '.js'))) {
DeferedRequirements::loadJS($themeName.'_'.$class . '.js');
}
return self::forTemplate();
@ -90,7 +91,8 @@ class DeferedRequirements implements TemplateGlobalProvider
public static function loadCSS($css)
{
if (self::$defered && !self::_webpackActive()) {
$external = (mb_substr($css, 0, 2) === '//' || mb_substr($css, 0, 4) === 'http');
if ($external || (self::$defered && !self::_webpackActive())) {
self::$css[] = $css;
} else {
WebpackTemplateProvider::loadCSS($css);
@ -99,6 +101,9 @@ class DeferedRequirements implements TemplateGlobalProvider
public static function loadJS($js)
{
/*$external = (mb_substr($js, 0, 2) === '//' || mb_substr($js, 0, 4) === 'http');
if ($external || (self::$defered && !self::_webpackActive())) {*/
// webpack supposed to load external JS
if (self::$defered && !self::_webpackActive()) {
self::$js[] = $js;
} else {
@ -118,10 +123,6 @@ class DeferedRequirements implements TemplateGlobalProvider
public static function forTemplate()
{
if (!self::$defered || self::_webpackActive()) {
return false;
}
$result = '';
foreach (self::$css as $css) {
$result .= '<i class="defer-cs" data-load="' . self::get_url($css) . '"></i>';
@ -137,7 +138,7 @@ class DeferedRequirements implements TemplateGlobalProvider
.'function lscd(a){a<s.length-1&&(a++,lsc(s.item(a).getAttribute("data-load"),function(){lscd(a)}))}'
.'for(var s=document.getElementsByClassName("defer-cs"),i=0;i<s.length;i++){var b=document.createElement("link");b.rel="stylesheet",'
.'b.type="text/css",b.href=s.item(i).getAttribute("data-load"),b.media="all";var c=document.getElementsByTagName("body")[0];'
.'c.appendChild(b)}var s=document.getElementsByClassName("defer-sc"),i=0;lsc(s.item(i).getAttribute("data-load"),function(){lscd(i)});'
.'c.appendChild(b)}var s=document.getElementsByClassName("defer-sc"),i=0;if(s.item(i)!==null)lsc(s.item(i).getAttribute("data-load"),function(){lscd(i)});'
.'</script>';
return $result;

View File

@ -2,10 +2,11 @@
/**
* Directs assets requests to Webpack server or to static files
*/
*/
namespace Site\Templates;
use SilverStripe\Core\Manifest\ModuleManifest;
use SilverStripe\View\TemplateGlobalProvider;
use SilverStripe\View\Requirements;
use SilverStripe\Control\Director;
@ -27,7 +28,7 @@ class WebpackTemplateProvider implements TemplateGlobalProvider
/**
* @var string assets static files directory
*/
private static $dist = 'app/client/dist';
private static $dist = 'client/dist';
/**
* @return array
@ -38,7 +39,8 @@ class WebpackTemplateProvider implements TemplateGlobalProvider
'WebpackDevServer' => 'isActive',
'WebpackCSS' => 'loadCSS',
'WebpackJS' => 'loadJS',
'ResoursesURL' => 'resoursesURL',
'ResourcesURL' => 'resourcesURL',
'ProjectName' => 'themeName',
];
}
@ -48,6 +50,10 @@ class WebpackTemplateProvider implements TemplateGlobalProvider
*/
public static function loadCSS($path)
{
if (self::isActive()) {
return;
}
Requirements::css(self::_getPath($path));
}
@ -60,9 +66,14 @@ class WebpackTemplateProvider implements TemplateGlobalProvider
Requirements::javascript(self::_getPath($path));
}
public static function resoursesURL($link = null)
public static function themeName()
{
return Controller::join_links(Director::baseURL(), '/resources/app/client/dist/img/', $link);
return Config::inst()->get(ModuleManifest::class, 'project');
}
public static function resourcesURL($link = null)
{
return Controller::join_links(Director::baseURL(), '/resources/'.self::themeName().'/client/dist/img/', $link);
}
@ -100,6 +111,7 @@ class WebpackTemplateProvider implements TemplateGlobalProvider
{
return strpos($path, '//') === false ?
Controller::join_links(
self::themeName(),
Config::inst()->get(__CLASS__, 'DIST'),
(strpos($path, '.css') ? 'css' : 'js'),
$path

View File

@ -0,0 +1,89 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 10/31/18
* Time: 5:31 AM
*/
namespace Site\Tests;
use SilverStripe\Assets\Upload_Validator;
use SilverStripe\Core\Cache\FilesystemCacheFactory;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Dev\BuildTask;
use SilverStripe\Assets\File;
class TestServer extends BuildTask
{
protected $title = 'Test server';
protected $description = 'Test server';
public function run($request)
{
echo '<style>table{width:100%}table td,table th{border:1px solid #dedede}</style>';
echo '<h2>Testing Uploads</h2>';
$maxUpload = ini_get('upload_max_filesize');
$maxPost = ini_get('post_max_size');
echo self::success('PHP max upload size: '.$maxUpload);
echo self::success('PHP max post size: '.$maxPost);
echo self::success('So calculated max upload size: '.self::formatBytes(min(
Convert::memstring2bytes($maxUpload),
Convert::memstring2bytes($maxPost)
)));
$defaultSizes = Config::inst()->get(Upload_Validator::class, 'default_max_file_size');
if($defaultSizes) {
if(!is_array($defaultSizes)){
echo self::error('default_max_file_size miss-configuration, plz fix');
var_dump($defaultSizes);
die();
}
echo '<h3>Configured limits:</h3><table style="text-align:center">'
.'<thead><tr><th>File</th><th>Size limit</th></tr></thead><tbody>';
foreach ($defaultSizes as $k => $size) {
echo '<tr><td>'.$k.'</td><td>'.$size.'</td></tr>';
}
echo '</tbody></table>';
}
die();
}
public static function formatBytes($size, $precision = 2)
{
$base = log($size, 1024);
$suffixes = array('', 'K', 'M', 'G', 'T');
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[(string)floor($base)];
}
public static function error($text)
{
return '<span style="color:red">ERROR: '.$text.'</span><br/>';
}
public static function success($text)
{
return '<span style="color:green">SUCCESS: '.$text.'</span><br/>';
}
public static function warning($text)
{
return '<span style="color:#0014ff">WARNING: '.$text.'</span><br/>';
}
public static function renderValidation($result)
{
echo '<p>';
$msgs = $result->getMessages();
foreach ($msgs as $msg) {
echo self::error($msg['fieldName'].': '.$msg['message']);
}
echo '</p>';
}
}

View File

@ -2,7 +2,11 @@
<h1 class="page-header container<% if $ElementalArea.Elements.Count < 1 %> no-elements<% end_if %>">$Title</h1>
<div class="page-content">
$ElementalArea
<% if $CurrentElement %>
$CurrentElement
<% else %>
$ElementalArea
<% end_if %>
<% if $Form %>
<div class="container">

View File

@ -27,7 +27,10 @@
</div>
<div class="col-sm-6">
<% if $PrivacyPolicy %>
<a href="$PrivacyPolicy.Link" target="_blank">$PrivacyPolicy.Title</a>
<a href="$PrivacyPolicy.Link">$PrivacyPolicy.Title</a>
<% end_if %>
<% if $Sitemap %>
<a href="$Sitemap.Link">$Sitemap.Title</a>
<% end_if %>
</div>
</div>

View File

@ -1,6 +1,6 @@
<div class="row">
<div class="col-sm-6">
<a id="Logo" href="/"><img src="{$StaticPath('img/logo.png')}" alt="{$SiteConfig.Title}" /></a>
<a id="Logo" href="/"><img src="{$ResoursesURL('logo.png')}" alt="{$SiteConfig.Title}" /></a>
</div>
<div class="col-sm-6 text-right">
<% with $SiteConfig %>

View File

@ -0,0 +1 @@
<div></div>

View File

@ -0,0 +1,11 @@
<div id="uff">
<div class="on-complete-message user-form">
$OnCompleteMessage
</div>
<% if $CustomThankYouCode %>
<div class="custom-code user-form">
$CustomThankYouCode.RAW
</div>
<% end_if %>
</div>

View File

@ -4,7 +4,7 @@
"description": "The SilverStripe Framework Installer",
"require": {
"php": ">=7.1.0",
"silverstripe/recipe-cms": "1.2.x-dev",
"silverstripe/recipe-cms": "^4",
"wilr/silverstripe-googlesitemaps": "*",
"silverstripe/userforms": "*",
"undefinedoffset/sortablegridfield": "*",
@ -13,37 +13,34 @@
"jonom/silverstripe-betternavigator": "*",
"silverstripe/externallinks": "*",
"symbiote/silverstripe-gridfieldextensions": "*",
"colymba/gridfield-bulk-editing-tools": "3.0.0-beta4",
"colymba/gridfield-bulk-editing-tools": "^3",
"dnadesign/silverstripe-elemental-list": "*",
"dnadesign/silverstripe-elemental-virtual": "*",
"dnadesign/silverstripe-elemental-userforms": "*",
"dynamic/silverstripe-elemental-blocks": "*",
"drmartingonzo/ss-tinymce-charcount": "^1.0",
"drmartingonzo/ss-tinymce-charcount": "*",
"axllent/silverstripe-version-truncator": "*",
"firesphere/googlemapsfield": "^1.0@dev",
"gorriecoe/silverstripe-dataobjecthistory": "^1.2",
"mak001/silverstripe-categorization": "^1.0@dev",
"axllent/silverstripe-bootstrap-forms": "^2.0",
"silverstripe/redirectedurls": "dev-master",
"firesphere/googlemapsfield": "*",
"gorriecoe/silverstripe-dataobjecthistory": "*",
"axllent/silverstripe-bootstrap-forms": "*",
"silverstripe/redirectedurls": "*",
"undefinedoffset/silverstripe-nocaptcha": "*",
"a2nt/silverstripe-font-awesome-field": "dev-master",
"stevie-mayhew/silverstripe-svg": "^2.1",
"betterbrief/silverstripe-googlemapfield": "^2.1",
"innoweb/silverstripe-sitemap": "^2.0",
"silverstripe/multiuser-editing-alert": "^2.0",
"gorriecoe/silverstripe-link": "^1.2",
"gorriecoe/silverstripe-linkfield": "dev-master"
"stevie-mayhew/silverstripe-svg": "*",
"betterbrief/silverstripe-googlemapfield": "*",
"innoweb/silverstripe-sitemap": "*",
"silverstripe/multiuser-editing-alert": "*",
"gorriecoe/silverstripe-link": "*",
"gorriecoe/silverstripe-linkfield": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"lekoala/silverstripe-debugbar": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/a2nt/silverstripe-font-awesome"
}
],
"repositories": [{
"type": "vcs",
"url": "https://github.com/a2nt/silverstripe-font-awesome"
}],
"extra": {
"expose": [
"app/client/dist"

View File

@ -29,6 +29,8 @@
"bootstrap-offcanvas": "^1.0.0",
"bootstrap-select": "^1.13.2",
"bootstrap-timepicker": "^0.5.2",
"bootstrap-confirmation2": "^4.0.4",
"bootstrap-table": "^1.14.2",
"core-util-is": "^1.0.2",
"font-awesome": "^4.7.0",
"foundation-emails": "^2.2.1",

View File

View File

@ -0,0 +1,4 @@
import '../scss/app.scss';
import 'js/app';
import './_layout';

View File

@ -0,0 +1,2 @@
@import "~scss/app.scss";
@import "_layout";

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="$ContentLocale.ATT" dir="$i18nScriptDirection.ATT">
<%-- manifest="/cache.appcache" --%>
<head>
<% include MetaHead %>
</head>
<body oncontextmenu="return false;"<% with $SiteConfig %> data-default-lng="$Longitude" data-default-lat="$Latitude"<% end_with %>>
<%-- Upgrade your Browser notice --%>
<!--[if lt IE 10]><div class="main-bn"><a href="https://www.google.com/chrome/browser/desktop/" title="<%t Page.UPGRADEBROWSER 'Upgrade your browser' %>"><%t Page.OUTDATEDBROWSER 'You are using an outdated browser. For a faster, safer browsing experience, upgrade for free today.' %></a></div><![endif]-->
<%-- No JS enabled notice --%>
<noscript><div class="main-bn"><%t Page.JAVASCRIPTREQUIRED 'Please, enable javascript.' %></div></noscript>
<%-- Loading Spinner --%>
<div id="PageLoading"><div class="loading-spinner"><div class="bubblingG"><i id="bubblingG_1"></i><i id="bubblingG_2"></i><i id="bubblingG_3"></i></div><br/><%t Page.LOADINGTEXT 'LOADING ..' %></div></div>
<%-- Site Wide Alert Message --%>
<% include SiteWideMessage %>
<div class="wrapper">
<header id="Header" class="container">
<% include Header %>
</header>
<main id="MainContent" data-ajax-region="LayoutAjax">
$Layout
</main>
</div>
<footer id="Footer" class="site-footer">
<% include Footer %>
</footer>
<div class="hidden-print">
$BetterNavigator
</div>
<%-- Require CSS+JS from /public/resourses/[js,css]/[ClassName].[js,css] --%>
$AutoRequirements($ClassName).RAW
<%-- Mapbox --%>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.js"></script>
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.css" rel="stylesheet" />
<%-- place extra requirements after this line --%>
<div class="extra-code extra-code-site">
$SiteConfig.ExtraCode
</div>
</body>
</html>

View File

@ -6,46 +6,79 @@ const webpack = require('webpack');
const conf = require('./webpack.configuration');
const path = require('path');
const filesystem = require('fs');
const includes = {
app: path.join(__dirname, conf.SRC, 'js/app.js'),
const includes = {};
const _addAppFiles = (theme) => {
const dirPath = path.resolve(__dirname, theme);
const themeName = path.basename(theme);
if (filesystem.existsSync(path.join(dirPath, conf.SRC, 'js', 'app.js'))) {
includes[`${themeName}`] = path.join(dirPath, conf.SRC, 'js', 'app.js');
} else if (filesystem.existsSync(path.join(dirPath, conf.SRC, 'scss', 'app.scss'))) {
includes[`${themeName}`] = path.join(dirPath, conf.SRC, 'scss', 'app.scss');
}
const _getAllFilesFromFolder = function(dir, includeSubFolders = true) {
const dirPath = path.resolve(__dirname, dir);
let results = [];
filesystem.readdirSync(dirPath).forEach((file) => {
if (file.charAt(0) === '_') {
return;
}
const filePath = `${dirPath}/${file}`;
const stat = filesystem.statSync(filePath);
if (stat && stat.isDirectory() && includeSubFolders) {
results = results.concat(_getAllFilesFromFolder(filePath, includeSubFolders));
} else {
results.push(filePath);
}
});
return results;
};
// add page specific scripts
const typesJSPath = path.join(theme, conf.TYPESJS);
if (filesystem.existsSync(typesJSPath)) {
const pageScripts = _getAllFilesFromFolder(typesJSPath, true);
pageScripts.forEach((file) => {
includes[`${themeName}_${path.basename(file, '.js')}`] = file;
});
}
// add page specific scss
const typesSCSSPath = path.join(theme, conf.TYPESSCSS);
if (filesystem.existsSync(typesSCSSPath)) {
const scssIncludes = _getAllFilesFromFolder(typesSCSSPath, true);
scssIncludes.forEach((file) => {
includes[`${themeName}_${path.basename(file, '.scss')}`] = file;
});
}
};
const _getAllFilesFromFolder = function(dir) {
dir = path.resolve(__dirname, dir);
_addAppFiles(conf.APPDIR);
const filesystem = require('fs');
let results = [];
// add themes
if (conf.THEMESDIR) {
const dir = path.resolve(__dirname, conf.THEMESDIR);
filesystem.readdirSync(dir).forEach((file) => {
if (file === '_notes') {
return;
}
if (filesystem.existsSync(dir)) {
filesystem.readdirSync(dir).forEach((file) => {
filePath = `${dir}/${file}`;
const stat = filesystem.statSync(filePath);
file = `${dir}/${file}`;
const stat = filesystem.statSync(file);
if (stat && stat.isDirectory()) {
results = results.concat(_getAllFilesFromFolder(file));
} else {
results.push(file);
}
});
return results;
};
// add page specific scripts
const pageScripts = _getAllFilesFromFolder(conf.TYPESJS);
pageScripts.forEach((file) => {
includes[path.basename(file, '.js')] = file;
});
// add page specific scss
const scssIncludes = _getAllFilesFromFolder(conf.TYPESSCSS);
scssIncludes.forEach((file) => {
includes[path.basename(file, '.scss')] = file;
});
if (stat && stat.isDirectory()) {
_addAppFiles(path.join(conf.THEMESDIR, file));
}
});
}
}
module.exports = {
entry: includes,
@ -79,14 +112,6 @@ module.exports = {
}, {
test: /\.coffee?$/,
use: 'coffee-loader',
}, {
test: /\.(png|jpg|jpeg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'img/',
publicPath: '../img/',
},
}, {
test: /\.worker\.js$/,
use: {
@ -97,7 +122,12 @@ module.exports = {
resolve: {
modules: [
path.resolve(__dirname, 'public'),
'node_modules'
path.resolve(__dirname, conf.APPDIR, 'client', 'src'),
path.resolve(__dirname, conf.APPDIR, 'client', 'src', 'js'),
path.resolve(__dirname, conf.APPDIR, 'client', 'src', 'scss'),
path.resolve(__dirname, conf.APPDIR, 'client', 'src', 'img'),
path.resolve(__dirname, conf.APPDIR, 'client', 'src', 'thirdparty'),
path.resolve(__dirname, 'node_modules')
],
alias: {
'jquery': require.resolve('jquery'),

View File

@ -25,7 +25,7 @@ const config = merge.strategy({
},
output: {
path: path.join(__dirname, conf.DIST),
path: path.join(__dirname),
filename: '[name].js',
// necessary for HMR to know where to load the hot update chunks
publicPath: 'https://' + conf.HOSTNAME + ':' + conf.PORT + '/'
@ -80,7 +80,12 @@ const config = merge.strategy({
}, {
test: /\.(gif|png|jpg|jpeg|ttf|otf|eot|svg|woff(2)?)$/,
use: [{
loader: 'url-loader'
loader: 'file-loader',
options: {
name(file) {
return 'public/[path][name].[ext]';
},
},
}]
}]
},
@ -98,7 +103,9 @@ const config = merge.strategy({
clientLogLevel: 'info',
contentBase: [
path.resolve(__dirname, 'public'),
'node_modules'
path.resolve(__dirname, 'public', 'resources'),
path.resolve(__dirname, 'public', 'resources', conf.APPDIR, conf.DIST),
'node_modules',
],
//watchContentBase: true,
overlay: {
@ -106,9 +113,9 @@ const config = merge.strategy({
errors: true
},
headers: {
'Access-Control-Allow-Origin': '*'
'Access-Control-Allow-Origin': '*',
}
},
});
module.exports = config;
module.exports = config;

View File

@ -17,9 +17,9 @@ module.exports = merge(common, {
devtool: '',
output: {
path: path.join(__dirname, conf.DIST),
filename: 'js/[name].js',
publicPath: conf.DIST + '/',
path: path.join(__dirname, conf.APPDIR, conf.DIST),
filename: path.join('js', '[name].js'),
publicPath: path.join(conf.APPDIR, conf.DIST),
},
module: {
@ -82,7 +82,20 @@ module.exports = merge(common, {
publicPath: '../fonts/'
}
}]
}]
}, {
test: /\.(png|jpg|jpeg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'img/',
publicPath: '../img/'
/*,
name(file) {
//return 'public/[path][name].[ext]';
return '[hash].[ext]';
},*/
},
}, ]
},
plugins: [
@ -106,9 +119,9 @@ module.exports = merge(common, {
}),
new FaviconsWebpackPlugin({
logo: path.join(__dirname, conf.SRC) + '/favicon.png',
logo: path.join(__dirname, conf.APPDIR, conf.SRC, 'favicon.png'),
prefix: '/icons/',
statsFilename: conf.DIST + '/icons/iconstats.json',
statsFilename: path.join(conf.APPDIR, conf.DIST, 'icons', 'iconstats.json'),
icons: {
android: true,
appleIcon: true,