mirror of
https://github.com/a2nt/cms-niceties.git
synced 2024-10-22 11:05:46 +02:00
Initial
This commit is contained in:
parent
dca97bb394
commit
3c0d9263c5
8
.idea/cms-niceties.iml
generated
Normal file
8
.idea/cms-niceties.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/cms-niceties.iml" filepath="$PROJECT_DIR$/.idea/cms-niceties.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
39
_config.php
Executable file
39
_config.php
Executable file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use SilverStripe\Forms\HTMLEditor\HtmlEditorConfig;
|
||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||
use SilverStripe\ORM\Search\FulltextSearchable;
|
||||
/*use SilverStripe\View\Parsers\ShortcodeParser;
|
||||
use A2nt\CMSNiceties\Extensions\EmbedShortcodeProvider;*/
|
||||
|
||||
// setup TinyMCE editor
|
||||
$config = HtmlEditorConfig::get('cms');
|
||||
$config->enablePlugins([
|
||||
'template',
|
||||
'fullscreen',
|
||||
'hr',
|
||||
'contextmenu',
|
||||
'charmap',
|
||||
'visualblocks',
|
||||
'lists',
|
||||
'charcount' => ModuleResourceLoader::resourceURL(
|
||||
'drmartingonzo/ss-tinymce-charcount:client/dist/js/bundle.js'
|
||||
),
|
||||
]);
|
||||
$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();
|
||||
|
||||
// replace embed parser
|
||||
/*$parser = ShortcodeParser::get('default');
|
||||
$parser->unregister('embed');
|
||||
$parser->register('embed', [EmbedShortcodeProvider::class, 'handle_shortcode']);*/
|
28
_config/base-config.yml
Executable file
28
_config/base-config.yml
Executable file
@ -0,0 +1,28 @@
|
||||
---
|
||||
Name: webapp-base-config
|
||||
---
|
||||
SilverStripe\Core\Manifest\ModuleManifest:
|
||||
project: app
|
||||
|
||||
Page:
|
||||
default_container_class: 'container'
|
||||
|
||||
#SilverStripe\Admin\LeftAndMain:
|
||||
# extra_requirements_javascript:
|
||||
# - 'colymba/gridfield-bulk-editing-tools:client/dist/js/main.js'
|
||||
# - 'colymba/gridfield-bulk-editing-tools:client/lang/en.js'
|
||||
# extra_requirements_css:
|
||||
# - 'colymba/gridfield-bulk-editing-tools:client/dist/styles/main.css'
|
||||
|
||||
SilverStripe\Admin\LeftAndMain:
|
||||
extra_requirements_javascript:
|
||||
- 'app/client/dist/js/app_cms.js'
|
||||
extra_requirements_css:
|
||||
- 'app/client/dist/css/app_cms.css'
|
||||
|
||||
SilverStripe\Forms\HTMLEditor\TinyMCEConfig:
|
||||
editor_css:
|
||||
- 'app/client/dist/css/app_editor.css'
|
||||
|
||||
SilverStripe\Control\Email\Email:
|
||||
send_all_emails_from: noreply@twma.pro
|
47
_config/base-extensions.yml
Executable file
47
_config/base-extensions.yml
Executable file
@ -0,0 +1,47 @@
|
||||
---
|
||||
Name: webapp-base-extensions
|
||||
---
|
||||
# Basic extensions
|
||||
SilverStripe\Admin\LeftAndMain:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\LeftAndMainExtension
|
||||
|
||||
SilverStripe\SiteConfig\SiteConfig:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\SocialExtension
|
||||
- A2nt\CMSNiceties\Extensions\SiteConfigExtension
|
||||
- A2nt\CMSNiceties\Extensions\NotificationsExtension
|
||||
|
||||
SilverStripe\CMS\Model\SiteTree:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\SiteTreeExtension
|
||||
|
||||
Sheadawson\Linkable\Models\EmbeddedObject:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\EmbeddedObjectExtension
|
||||
|
||||
SilverStripe\Assets:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\ImageExtension
|
||||
|
||||
Dynamic\FlexSlider\Model\SlideImage:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\SlideImageExtension
|
||||
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
#SilverStripe\UserForms\Model\UserDefinedForm:
|
||||
# class: A2nt\CMSNiceties\Extensions\UserDefinedForm_HiddenClass
|
||||
Sheadawson\Linkable\Forms\EmbeddedObjectField:
|
||||
class: A2nt\CMSNiceties\Extensions\EmbedObjectField
|
||||
SilverStripe\Forms\CompositeField:
|
||||
class: A2nt\CMSNiceties\Extensions\CompositeFieldExtension
|
||||
|
||||
SilverStripe\UserForms\Form\UserForm:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\PlaceholderFormExtension
|
||||
|
||||
Page:
|
||||
searchable_objects:
|
||||
- A2nt\CMSNiceties\Models\TeamMember
|
||||
extensions:
|
||||
- DNADesign\Elemental\Extensions\ElementalPageExtension
|
81
_config/base-files.yml
Executable file
81
_config/base-files.yml
Executable file
@ -0,0 +1,81 @@
|
||||
---
|
||||
Name: webapp-base-files
|
||||
---
|
||||
SilverStripe\Blog\Model\BlogPost:
|
||||
featured_images_directory: 'blog-posts'
|
||||
|
||||
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'
|
38
_config/base-graphql.yml
Normal file
38
_config/base-graphql.yml
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
Name: webapp-base-graphql
|
||||
After: graphqlconfig
|
||||
---
|
||||
SilverStripe\Control\Director:
|
||||
rules:
|
||||
'graphql': '%$SilverStripe\GraphQL\Controller.default'
|
||||
|
||||
SilverStripe\GraphQL\Controller:
|
||||
cors:
|
||||
Enabled: true
|
||||
Allow-Origin: '*'
|
||||
Allow-Headers: 'Authorization, Content-Type, Content-Language, apikey'
|
||||
Allow-Methods: 'GET, PUT, DELETE, OPTIONS, POST'
|
||||
#Allow-Credentials: 'true'
|
||||
Max-Age: 600 # 600 seconds = 10 minutes.
|
||||
|
||||
SilverStripe\GraphQL\Auth\Handler:
|
||||
authenticators:
|
||||
- class: A2nt\CMSNiceties\GraphQL\APIKeyAuthenticator
|
||||
priority: 30
|
||||
|
||||
SilverStripe\GraphQL\Manager.default:
|
||||
properties:
|
||||
Middlewares:
|
||||
APIKeyMiddleware: A2nt\CMSNiceties\GraphQL\APIKeyMiddleware
|
||||
|
||||
SilverStripe\GraphQL\Manager:
|
||||
schemas:
|
||||
default:
|
||||
types:
|
||||
member: 'A2nt\CMSNiceties\GraphQL\MemberTypeCreator'
|
||||
page: 'A2nt\CMSNiceties\GraphQL\PageTypeCreator'
|
||||
element: 'A2nt\CMSNiceties\GraphQL\ElementTypeCreator'
|
||||
queries:
|
||||
readPages: 'A2nt\CMSNiceties\GraphQL\PaginatedReadPagesQueryCreator'
|
||||
readMembers: 'A2nt\CMSNiceties\GraphQL\ReadMembersQueryCreator'
|
||||
paginatedReadMembers: 'A2nt\CMSNiceties\GraphQL\PaginatedReadMembersQueryCreator'
|
60
_config/base-logs.yml_
Executable file
60
_config/base-logs.yml_
Executable file
@ -0,0 +1,60 @@
|
||||
---
|
||||
Name: webapp-base-logs-dev
|
||||
Only:
|
||||
environment: dev
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
Psr\Log\LoggerInterface.errorhandler:
|
||||
calls:
|
||||
pushMyDisplayErrorHandler: [pushHandler, ['%$DisplayErrorHandler']]
|
||||
DisplayErrorHandler:
|
||||
class: SilverStripe\Logging\HTTPOutputHandler
|
||||
constructor:
|
||||
- 'notice'
|
||||
properties:
|
||||
Formatter: '%$SilverStripe\Logging\DetailedErrorFormatter'
|
||||
CLIFormatter: '%$SilverStripe\Logging\DetailedErrorFormatter'
|
||||
---
|
||||
Name: webapp-base-logs-live
|
||||
Except:
|
||||
environment: dev
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
# Default logger implementation for general purpose use
|
||||
Psr\Log\LoggerInterface:
|
||||
calls:
|
||||
# Save system logs to file
|
||||
pushFileLogHandler: [pushHandler, ['%$LogFileHandler']]
|
||||
|
||||
# Core error handler for system use
|
||||
Psr\Log\LoggerInterface.errorhandler:
|
||||
calls:
|
||||
# Save errors to file
|
||||
pushFileLogHandler: [pushHandler, ['%$LogFileHandler']]
|
||||
# Format and display errors in the browser/CLI
|
||||
pushMyDisplayErrorHandler: [pushHandler, ['%$DisplayErrorHandler']]
|
||||
|
||||
# Custom handler to log to a file
|
||||
LogFileHandler:
|
||||
class: Monolog\Handler\StreamHandler
|
||||
constructor:
|
||||
- '../silverstripe.log'
|
||||
- 'notice'
|
||||
properties:
|
||||
Formatter: '%$Monolog\Formatter\HtmlFormatter'
|
||||
ContentType: text/html
|
||||
|
||||
# Handler for displaying errors in the browser or CLI
|
||||
DisplayErrorHandler:
|
||||
class: SilverStripe\Logging\HTTPOutputHandler
|
||||
constructor:
|
||||
- 'error'
|
||||
properties:
|
||||
Formatter: '%$SilverStripe\Logging\DebugViewFriendlyErrorFormatter'
|
||||
|
||||
# Configuration for the "friendly" error formatter
|
||||
SilverStripe\Logging\DebugViewFriendlyErrorFormatter:
|
||||
class: SilverStripe\Logging\DebugViewFriendlyErrorFormatter
|
||||
properties:
|
||||
Title: 'There has been an error'
|
||||
Body: 'The website server has not been able to respond to your request'
|
9
_config/base-mimevalidator.yml
Normal file
9
_config/base-mimevalidator.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
Name: webapp-base-mimeuploadvalidator
|
||||
After: '#mimeuploadvalidator'
|
||||
Only:
|
||||
moduleexists: 'silverstripe/mimevalidator'
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Assets\Upload_Validator:
|
||||
class: SilverStripe\MimeValidator\MimeUploadValidator
|
105
_config/base-security.yml
Executable file
105
_config/base-security.yml
Executable file
@ -0,0 +1,105 @@
|
||||
---
|
||||
Name: 'webapp-base-security'
|
||||
After: 'framework/*, cms/*, security_baseline'
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Security\MemberAuthenticator\LostPasswordHandler:
|
||||
class: A2nt\CMSNiceties\Extensions\LostPasswordHandlerExtension
|
||||
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
|
||||
class: A2nt\CMSNiceties\Extensions\SiteMemberLoginForm
|
||||
---
|
||||
Except:
|
||||
environment: dev
|
||||
---
|
||||
# Secure cookies
|
||||
SilverStripe\Control\Session:
|
||||
cookie_secure: true
|
||||
strict_user_agent_check: false
|
||||
timeout: 604800
|
||||
|
||||
SilverStripe\Forms\PasswordField:
|
||||
autocompleate: false
|
||||
|
||||
SilverStripe\Security\Member:
|
||||
lock_out_after_incorrect_logins: 5
|
||||
lock_out_delay_mins: 5
|
||||
# Password expiry should only happen when the password is leaked (optionally expire automatically if PCI/NIST compliance is required)
|
||||
# password_expiry_days: 90
|
||||
# instead of password change, we send out a notice on change of password OR Email (notify_account_security_change)
|
||||
notify_password_change: false
|
||||
#######################
|
||||
# Security Headers
|
||||
#######################
|
||||
#Controller:
|
||||
# security_headers:
|
||||
# # # Values may contain :security_reporting_base_url: placeholders, will be replaced with the URL to SecurityBaselineController endpoint
|
||||
# # Header-Directive: "value; another value;"
|
||||
# # X-Version-Alias-Of-Same-Header: "x:Header-Directive" # 'x-alias' headers may be aliased to the standard by a value starting with "x:Standard"
|
||||
# # X-Another-Alias-Version-Of-Same: "different; value syntax as well;"
|
||||
|
||||
#
|
||||
# A useful base from guttmann/silverstripe-security-headers - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#Security:
|
||||
#
|
||||
|
||||
# # Content-Security-Policy - https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
||||
# # Specifies approved sources of content that the browser may load from your website
|
||||
# # Useful: upgrade-insecure-requests; (Instructs browser to treat a site's insecure URLs as if they are HTTPS (eg for legacy sites)
|
||||
# # Example: Allow everything but only from the same origin:
|
||||
# Content-Security-Policy: "default-src 'self';"
|
||||
# # Example: Allow Google Analytics, Google AJAX CDN and Same Origin
|
||||
# Content-Security-Policy: "script-src 'self' www.google-analytics.com ajax.googleapis.com;"
|
||||
# # Example: Starter Policy - allows images, scripts, AJAX, form actions, and CSS from the same origin, and does not allow any
|
||||
# # other resources to load (eg object, frame, media, etc). It is a good starting point for many sites.
|
||||
# Content-Security-Policy: "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'"
|
||||
|
||||
# # Content-Security-Policy-Report-Only - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||
# # Allows web developers to experiment with policies by monitoring (but not enforcing) their effects
|
||||
# # Browsers capable of enforcing CSP will send a violation report as a POST request to report-uri
|
||||
# Content-Security-Policy-Report-Only: default-src https:; report-uri /security-reporting-endpoint/csp/
|
||||
# Content-Security-Policy-Report-Only: "default-src https:; script-src 'self' https: 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; img-src 'self' https: data:; style-src 'self' 'unsafe-inline'; base-uri 'self'; form-action 'self'; report-uri /security-reporting-endpoint/csp/;"
|
||||
|
||||
# # Strict-Transport-Security - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
# # Tells the browser to ONLY interact with the site using HTTPS and never HTTP
|
||||
# Strict-Transport-Security: "max-age=31536000" # time in seconds (one year=31536000) to remember that the site is only accessible over HTTPS
|
||||
|
||||
# # Frame-Options - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# # Disallowes pages to render within a frame - protects against clickjacking attacks
|
||||
# Frame-Options: "deny"
|
||||
|
||||
# # XSS-Protection - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# # protect against Cross-site Scripting attacks (value 1=sanitize (default in most browsers), set to "1; mode=block" to prevent rendering if attack is detected)
|
||||
# # Deprecated: if you do not need to support legacy browsers, it is recommended that you use Content-Security-Policy without allowing unsafe-inline scripts instead
|
||||
# X-XSS-Protection: "1; mode=block"
|
||||
|
||||
# # X-Content-Type-Options - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# # Indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed
|
||||
# # NOTE: Opting out of MIME sniffing can cause HTML web pages to be downloaded instead of rendered when they are
|
||||
# # served with a MIME type other than text/html. Make sure to set both headers correctly.
|
||||
# # Site security testers usually expect this header to be set.
|
||||
# X-Content-Type-Options: "nosniff"
|
||||
|
||||
#
|
||||
# Some more from https://help.dreamhost.com/hc/en-us/articles/360036486952-Security-headers
|
||||
#
|
||||
|
||||
# # Referrer-Policy - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
|
||||
# # controls how much referrer information should be sent to another server
|
||||
# Referrer-Policy: no-referrer
|
||||
|
||||
# # Feature-Policy - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
|
||||
# # (Experimental 2020) controls which browser features are allowed on your website, eg for sites allowing third-party content
|
||||
|
||||
# # CORS - Allow resource sharing with another domain (eg webfonts & ajax requests)
|
||||
# # Access-Control-Allow-Origin - developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||
|
||||
#
|
||||
# A further selection from https://github.com/bepsvpt/secure-headers/blob/master/config/secure-headers.php
|
||||
#
|
||||
|
||||
# Clear-Site-Data - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
|
||||
# Clears browsing data (cookies, storage, cache) associated with the requesting website
|
||||
|
||||
# Expect-CT - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT
|
||||
# Lets sites opt in to reporting and/or enforcement of Certificate Transparency requirements,
|
||||
# to prevent the use of misissued certificates for that site from going unnoticed.
|
||||
# (will likely become obsolete in June 2021)
|
8
_config/base-version-truncator.yml
Executable file
8
_config/base-version-truncator.yml
Executable file
@ -0,0 +1,8 @@
|
||||
---
|
||||
Name: 'webapp-base-version-truncator'
|
||||
---
|
||||
Axllent\VersionTruncator\VersionTruncator:
|
||||
keep_versions: 4 # how many (published) versions of each page to keep
|
||||
keep_drafts: 2 # how many drafts of each page to keep
|
||||
keep_redirects: true # keep page versions that have a different URLSegment (for redirects)
|
||||
keep_old_page_types: false # keep page versions where page type (ClassName) has changed
|
12
_config/debugbar.yml
Executable file
12
_config/debugbar.yml
Executable file
@ -0,0 +1,12 @@
|
||||
---
|
||||
Name: 'webapp-debugbar'
|
||||
After:
|
||||
- 'framework'
|
||||
- 'debugbar'
|
||||
Only:
|
||||
environment: 'dev'
|
||||
---
|
||||
LeKoala\DebugBar\DebugBar:
|
||||
enabled_in_admin: false
|
||||
query_limit: 500
|
||||
max_header_length: 2048
|
28
_config/env-check.yml
Executable file
28
_config/env-check.yml
Executable file
@ -0,0 +1,28 @@
|
||||
---
|
||||
Name: webapp-env-check
|
||||
---
|
||||
|
||||
SilverStripe\EnvironmentCheck\EnvironmentCheckSuite:
|
||||
registered_checks:
|
||||
curl:
|
||||
definition: 'HasFunctionCheck("curl_init")'
|
||||
title: 'is curl available?'
|
||||
gd:
|
||||
definition: 'HasFunctionCheck("imagecreatetruecolor")'
|
||||
title: 'Does PHP have GD2 support?'
|
||||
db:
|
||||
definition: 'DatabaseCheck("Page")'
|
||||
title: 'Is the database accessible?'
|
||||
url:
|
||||
definition: 'URLCheck()'
|
||||
title: 'Is the homepage accessible?'
|
||||
registered_suites:
|
||||
check:
|
||||
- curl
|
||||
- gd
|
||||
- db
|
||||
- url
|
||||
health:
|
||||
- db
|
||||
- url
|
||||
|
42
_config/locale-fluent.yml_
Executable file
42
_config/locale-fluent.yml_
Executable file
@ -0,0 +1,42 @@
|
||||
---
|
||||
Name: webapp-locale-fluent
|
||||
After:
|
||||
- webapp-extensions
|
||||
- webapp-elemental
|
||||
- webapp-locale
|
||||
---
|
||||
# Define Fluent locales
|
||||
TractorCow\Fluent\Model\Locale:
|
||||
default_records:
|
||||
en:
|
||||
Title: 'EN'
|
||||
Locale: en_US
|
||||
URLSegment: en
|
||||
IsGlobalDefault: 1
|
||||
us:
|
||||
Locale: en_US
|
||||
Title: 'EN'
|
||||
URLSegment: en
|
||||
ru:
|
||||
Locale: ru_RU
|
||||
Title: 'RU'
|
||||
URLSegment: ru
|
||||
Fallbacks:
|
||||
- =>TractorCow\Fluent\Model\Locale.us
|
||||
|
||||
# Enable Fluent extensions
|
||||
Page:
|
||||
extensions:
|
||||
- DNADesign\Elemental\TopPage\SiteTreeExtension
|
||||
- DNADesign\Elemental\Extensions\ElementalPageExtension
|
||||
- A2nt\CMSNiceties\Extensions\PageFluentExtension
|
||||
|
||||
DNADesign\Elemental\Models\ElementalArea:
|
||||
extensions:
|
||||
- DNADesign\Elemental\TopPage\FluentExtension
|
||||
- A2nt\CMSNiceties\Extensions\ElementalArea
|
||||
|
||||
DNADesign\Elemental\Models\BaseElement:
|
||||
extensions:
|
||||
- DNADesign\Elemental\TopPage\FluentExtension
|
||||
- A2nt\CMSNiceties\Extensions\ElementRows
|
59
_config/locale.yml
Executable file
59
_config/locale.yml
Executable file
@ -0,0 +1,59 @@
|
||||
---
|
||||
Name: webapp-locale
|
||||
---
|
||||
|
||||
Symbiote\Addressable\Addressable:
|
||||
allowed_countries:
|
||||
'us': 'United States'
|
||||
allowed_states:
|
||||
'AL': 'Alabama'
|
||||
'AK': 'Alaska'
|
||||
'AZ': 'Arizona'
|
||||
'AR': 'Arkansas'
|
||||
'CA': 'California'
|
||||
'CO': 'Colorado'
|
||||
'CT': 'Connecticut'
|
||||
'DE': 'Delaware'
|
||||
'DC': 'District Of Columbia'
|
||||
'FL': 'Florida'
|
||||
'GA': 'Georgia'
|
||||
'HI': 'Hawaii'
|
||||
'ID': 'Idaho'
|
||||
'IL': 'Illinois'
|
||||
'IN': 'Indiana'
|
||||
'IA': 'Iowa'
|
||||
'KS': 'Kansas'
|
||||
'KY': 'Kentucky'
|
||||
'LA': 'Louisiana'
|
||||
'ME': 'Maine'
|
||||
'MD': 'Maryland'
|
||||
'MA': 'Massachusetts'
|
||||
'MI': 'Michigan'
|
||||
'MN': 'Minnesota'
|
||||
'MS': 'Mississippi'
|
||||
'MO': 'Missouri'
|
||||
'MT': 'Montana'
|
||||
'NE': 'Nebraska'
|
||||
'NV': 'Nevada'
|
||||
'NH': 'New Hampshire'
|
||||
'NJ': 'New Jersey'
|
||||
'NM': 'New Mexico'
|
||||
'NY': 'New York'
|
||||
'NC': 'North Carolina'
|
||||
'ND': 'North Dakota'
|
||||
'OH': 'Ohio'
|
||||
'OK': 'Oklahoma'
|
||||
'OR': 'Oregon'
|
||||
'PA': 'Pennsylvania'
|
||||
'RI': 'Rhode Island'
|
||||
'SC': 'South Carolina'
|
||||
'SD': 'South Dakota'
|
||||
'TN': 'Tennessee'
|
||||
'TX': 'Texas'
|
||||
'UT': 'Utah'
|
||||
'VT': 'Vermont'
|
||||
'VA': 'Virginia'
|
||||
'WA': 'Washington'
|
||||
'WV': 'West Virginia'
|
||||
'WI': 'Wisconsin'
|
||||
'WY': 'Wyoming'
|
52
_config/options-elements.yml
Executable file
52
_config/options-elements.yml
Executable file
@ -0,0 +1,52 @@
|
||||
---
|
||||
Name: webapp-options-elements
|
||||
After:
|
||||
- elemental
|
||||
- elemental-list
|
||||
- elementalvirtual
|
||||
- webapp-base-extensions
|
||||
---
|
||||
Page:
|
||||
searchable_elements:
|
||||
- DNADesign\Elemental\Models\ElementContent
|
||||
extensions:
|
||||
- DNADesign\Elemental\Extensions\ElementalPageExtension
|
||||
|
||||
SilverStripe\CMS\Model\SiteTree:
|
||||
allowed_elements:
|
||||
- DNADesign\ElementalList\Model\ElementList
|
||||
- DNADesign\Elemental\Models\ElementContent
|
||||
- DNADesign\ElementalUserForms\Model\ElementForm
|
||||
- Dynamic\Elements\Image\Elements\ElementImage
|
||||
- Dynamic\Elements\Blog\Elements\ElementBlogPosts
|
||||
- Dynamic\Elements\Oembed\Elements\ElementOembed
|
||||
- Dynamic\Elements\Elements\ElementTestimonials
|
||||
#- A2nt\ElementalBasics\Elements\TeamMembersElement
|
||||
- A2nt\ElementalBasics\Elements\SliderElement
|
||||
- A2nt\ElementalBasics\Elements\MapElement
|
||||
#- A2nt\ElementalBasics\Elements\AccordionElement
|
||||
- DNADesign\ElementalVirtual\Model\ElementVirtual
|
||||
- A2nt\ElementalBasics\Elements\AccordionElement
|
||||
- A2nt\ElementalBasics\Elements\CustomSnippetElement
|
||||
- A2nt\ElementalBasics\Elements\InstagramElement
|
||||
|
||||
DNADesign\ElementalList\Model\ElementList:
|
||||
inline_editable: false
|
||||
default_global_elements: false
|
||||
allowed_elements:
|
||||
- DNADesign\ElementalList\Model\ElementList
|
||||
- DNADesign\Elemental\Models\ElementContent
|
||||
- DNADesign\ElementalUserForms\Model\ElementForm
|
||||
- Dynamic\Elements\Image\Elements\ElementImage
|
||||
- Dynamic\Elements\Blog\Elements\ElementBlogPosts
|
||||
- Dynamic\Elements\Oembed\Elements\ElementOembed
|
||||
- Dynamic\Elements\Elements\ElementTestimonials
|
||||
#- A2nt\ElementalBasics\Elements\TeamMembersElement
|
||||
- A2nt\ElementalBasics\Elements\SliderElement
|
||||
- A2nt\ElementalBasics\Elements\MapElement
|
||||
- A2nt\ElementalBasics\Elements\AccordionElement
|
||||
- A2nt\ElementalBasics\Elements\CustomSnippetElement
|
||||
- A2nt\ElementalBasics\Elements\InstagramElement
|
||||
styles:
|
||||
whiteframe: 'White Frame'
|
||||
noframe: 'No Frame'
|
33
_config/options-widgets.yml
Executable file
33
_config/options-widgets.yml
Executable file
@ -0,0 +1,33 @@
|
||||
---
|
||||
Name: webapp-options-widgets
|
||||
---
|
||||
# Blog + Widgets module extensions
|
||||
Page:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Widgets\WidgetPageExtension
|
||||
|
||||
SilverStripe\Blog\Model\Blog:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\BlogExtension
|
||||
SilverStripe\Blog\Model\BlogPost:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\BlogPostExtension
|
||||
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
icon: '<i class="icon font-icon-p-document"></i>'
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Widgets\WidgetExtension
|
||||
SilverStripe\Blog\Widgets\BlogArchiveWidget:
|
||||
icon: '<i class="icon font-icon-p-archive"></i>'
|
||||
SilverStripe\Blog\Widgets\BlogCategoriesWidget:
|
||||
icon: '<i class="icon font-icon-page-multiple"></i>'
|
||||
SilverStripe\Blog\Widgets\BlogFeaturedPostsWidget:
|
||||
icon: '<i class="icon font-icon-chart-line"></i>'
|
||||
SilverStripe\Blog\Widgets\BlogRecentPostsWidget:
|
||||
icon: '<i class="icon font-icon-back-in-time"></i>'
|
||||
SilverStripe\Blog\Widgets\BlogTagsCloudWidget:
|
||||
icon: '<i class="icon font-icon-tags"></i>'
|
||||
SilverStripe\Blog\Widgets\BlogTagsWidget:
|
||||
icon: '<i class="icon font-icon-tags"></i>'
|
||||
only_available_in:
|
||||
- CMSMain_HiddenClass
|
23
_config/shop.yml_
Executable file
23
_config/shop.yml_
Executable file
@ -0,0 +1,23 @@
|
||||
---
|
||||
Name: webapp-shop
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverShop\Checkout\SinglePageCheckoutComponentConfig:
|
||||
class: A2nt\CMSNiceties\Models\CheckoutNoDeliveryConfig
|
||||
|
||||
SilverShop\Extension\ShopConfigExtension:
|
||||
base_currency: USD
|
||||
|
||||
SilverShop\Model\Address:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\AddressExtension
|
||||
|
||||
SilverShop\Cart\ShoppingCartController:
|
||||
extensions:
|
||||
- A2nt\CMSNiceties\Extensions\ShoppingCartControllerExtension
|
||||
|
||||
A2nt\CMSNiceties\Templates\DeferedRequirements:
|
||||
custom_requirements:
|
||||
SilverShop\Page\AccountPageController:
|
||||
- SilverShop.Page.CheckoutPageController.js
|
||||
- SilverShop.Page.CheckoutPageController.css
|
41
_config/templates-requirements.yml
Executable file
41
_config/templates-requirements.yml
Executable file
@ -0,0 +1,41 @@
|
||||
---
|
||||
Name: webapp-templates-requirements
|
||||
---
|
||||
App\Templates\DeferredRequirements:
|
||||
nofontawesome: false
|
||||
version: false
|
||||
static_domain: false
|
||||
deferred: true
|
||||
noreact: false
|
||||
nojquery: true
|
||||
jquery_version: '3.4.1'
|
||||
|
||||
SilverStripe\FontAwesome\FontAwesomeField:
|
||||
version: '5.12.0'
|
||||
|
||||
SilverStripe\View\Requirements:
|
||||
disable_flush_combined: true
|
||||
SilverStripe\View\Requirements_Backend:
|
||||
combine_in_dev: true
|
||||
combine_hash_querystring: true
|
||||
default_combined_files_folder: 'combined'
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
# Create adapter that points to the custom directory root
|
||||
SilverStripe\Assets\Flysystem\PublicAdapter.custom-adapter:
|
||||
class: SilverStripe\Assets\Flysystem\PublicAssetAdapter
|
||||
constructor:
|
||||
Root: ./app/javascript
|
||||
# Set flysystem filesystem that uses this adapter
|
||||
League\Flysystem\Filesystem.custom-filesystem:
|
||||
class: 'League\Flysystem\Filesystem'
|
||||
constructor:
|
||||
Adapter: '%$SilverStripe\Assets\Flysystem\PublicAdapter.custom-adapter'
|
||||
# Create handler to generate assets using this filesystem
|
||||
SilverStripe\Assets\Storage\GeneratedAssetHandler.custom-generated-assets:
|
||||
class: SilverStripe\Assets\Flysystem\GeneratedAssets
|
||||
properties:
|
||||
Filesystem: '%$League\Flysystem\Filesystem.custom-filesystem'
|
||||
# Assign this generator to the requirements builder
|
||||
SilverStripe\View\Requirements_Backend:
|
||||
properties:
|
||||
AssetHandler: '%$SilverStripe\Assets\Storage\GeneratedAssetHandler.custom-generated-assets'
|
19
_config/templates-themes.yml
Executable file
19
_config/templates-themes.yml
Executable file
@ -0,0 +1,19 @@
|
||||
---
|
||||
Name: webapp-templates-themes
|
||||
After:
|
||||
- webapp-options-elements
|
||||
---
|
||||
SilverStripe\View\SSViewer:
|
||||
source_file_comments: true
|
||||
themes:
|
||||
- '$public'
|
||||
- '$default'
|
||||
|
||||
App\Elements\SliderElement:
|
||||
slide_width: 2140
|
||||
slide_height: 700
|
||||
|
||||
# 2x container width to automatically resize images for 2K display
|
||||
App\Elements\Extensions\ElementRows:
|
||||
container_max_width: 2280
|
||||
column_class: 'col-block col-md'
|
21
_config/webpack.yml
Executable file
21
_config/webpack.yml
Executable file
@ -0,0 +1,21 @@
|
||||
# Name: webapp-webpack
|
||||
# that's important to place this file into /app/_config/webpack.yml
|
||||
# with all configuration variables presented
|
||||
# Cuz WebPack compiling script use it to set configuration
|
||||
|
||||
A2nt\CMSNiceties\Templates\WebpackTemplateProvider:
|
||||
APPDIR: './app'
|
||||
THEMESDIR: './themes'
|
||||
HOSTNAME: 127.0.0.1
|
||||
PORT: 3000
|
||||
SRC: client/src
|
||||
DIST: client/dist
|
||||
TYPESJS: client/src/js/types
|
||||
TYPESSCSS: client/src/scss/types
|
||||
webp: false
|
||||
NODE_ENV: production #production,development
|
||||
HTTPS: true
|
||||
injectClient: true
|
||||
GRAPHQL_URL: '/graphql'
|
||||
GRAPHQL_API_KEY: 'LgPaRkVPYa8IY7x3AjbLC8wx6oPPSlO01yPflFXecvQ'
|
||||
#STATIC_URL: 'http://127.0.0.1'
|
27
composer.json
Normal file
27
composer.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "a2nt/cms-niceties",
|
||||
"description": "Some useful CMS updates",
|
||||
"type": "silverstripe-vendormodule",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"elemental"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [{
|
||||
"name": "Tony Air",
|
||||
"email": "tony@twma.pro"
|
||||
}],
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"silverstripe/cms": "^4",
|
||||
"a2nt/silverstripe-elemental-basics": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"A2nt\\ElementalBasics\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
31
lang/en.yml
Executable file
31
lang/en.yml
Executable file
@ -0,0 +1,31 @@
|
||||
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!'
|
||||
UPGRADEBROWSER: 'Upgrade your browser'
|
||||
OUTDATEDBROWSER: 'You are using an outdated browser. For a faster, safer browsing experience, upgrade for free today.'
|
||||
SilverShop\Model\Address:
|
||||
db_Surname: 'Last Name'
|
||||
SilverShop\Model\Order:
|
||||
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'
|
||||
Dynamic\FlexSlider\Model\SlideImage:
|
||||
SINGULARNAME: 'Slide'
|
||||
PLURALNAME: 'Slides'
|
||||
Addressable:
|
||||
SUBURB: 'City'
|
||||
Symbiote\AddressableAddressable:
|
||||
SUBURB: 'City'
|
||||
|
19
lang/ru.yml
Executable file
19
lang/ru.yml
Executable file
@ -0,0 +1,19 @@
|
||||
ru:
|
||||
Page:
|
||||
LOADINGTEXT: "ЗАГРУЗКА .."
|
||||
JAVASCRIPTREQUIRED: "Для корректной работы страницы требуется включить Javascript!"
|
||||
UPGRADEBROWSER: "Обновите браузер!"
|
||||
OUTDATEDBROWSER: "Вы используете устаревшую версию браузера. Обновите ваш браузер сейчас для повышения уровня безопасности вашей системы."
|
||||
PaginationLabel: "Переключение страниц"
|
||||
PaginationPrevious: "Назад"
|
||||
PaginationNext: "Вперед"
|
||||
SilverStripe\Blog\Model\Blog:
|
||||
PostedIn: "Категории"
|
||||
Tagged: "Отметки"
|
||||
Comments: "Коментарии"
|
||||
Posted: "Опубликовано"
|
||||
By: " - "
|
||||
AND: "и"
|
||||
LessThanAMinuteToRead: "Меньше минуты на чтение"
|
||||
MinutesToRead: "Минут на чтение"
|
||||
READMORE: "Подробнее"
|
29
src/Extensions/AddressExtension.php
Executable file
29
src/Extensions/AddressExtension.php
Executable file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 8/26/18
|
||||
* Time: 12:55 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
|
||||
class AddressExtension extends Extension
|
||||
{
|
||||
public function updateFormFields(FieldList $fields)
|
||||
{
|
||||
$holder = CompositeField::create();
|
||||
foreach ($fields as $field) {
|
||||
$holder->push($field);
|
||||
$fields->remove($field);
|
||||
}
|
||||
|
||||
$holder->addExtraClass('col-sm-6');
|
||||
$fields->push($holder);
|
||||
}
|
||||
}
|
19
src/Extensions/BlogExtension.php
Executable file
19
src/Extensions/BlogExtension.php
Executable file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class BlogExtension extends DataExtension
|
||||
{
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$f = $fields->dataFieldByName('ChildPages');
|
||||
if ($f) {
|
||||
$f->setConfig(GridFieldConfigBlogPost::create(75));
|
||||
}
|
||||
}
|
||||
}
|
27
src/Extensions/BlogPostExtension.php
Executable file
27
src/Extensions/BlogPostExtension.php
Executable file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 7/2/18
|
||||
* Time: 12:10 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use DNADesign\Elemental\Models\ElementContent;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class BlogPostExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'Featured' => 'Boolean(0)',
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$mainTab = $fields->findOrMakeTab('Root.Main');
|
||||
$mainTab->push(CheckboxField::create('Featured'));
|
||||
}
|
||||
}
|
17
src/Extensions/CMSMain_HiddenClass.php
Executable file
17
src/Extensions/CMSMain_HiddenClass.php
Executable file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 7/2/18
|
||||
* Time: 1:05 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\ORM\HiddenClass;
|
||||
use Page;
|
||||
|
||||
class CMSMain_HiddenClass extends Page implements HiddenClass
|
||||
{
|
||||
|
||||
}
|
26
src/Extensions/CompositeFieldExtension.php
Executable file
26
src/Extensions/CompositeFieldExtension.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost;
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class CompositeFieldExtension extends CompositeField
|
||||
{
|
||||
public function extraClass()
|
||||
{
|
||||
return 'composite '.parent::extraClass();
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
$attrs = parent::getAttributes();
|
||||
unset($attrs['name'], $attrs['type'], $attrs['disabled'], $attrs['readonly'], $attrs['autofocus']);
|
||||
|
||||
return $attrs;
|
||||
}
|
||||
}
|
47
src/Extensions/EmbedObjectField.php
Executable file
47
src/Extensions/EmbedObjectField.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
|
||||
use Sheadawson\Linkable\Forms\EmbeddedObjectField;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||
|
||||
class EmbedObjectField extends EmbeddedObjectField
|
||||
{
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return mixed|DBHTMLText
|
||||
*/
|
||||
public function FieldHolder($properties = [])
|
||||
{
|
||||
$name = $this->getName();
|
||||
|
||||
$fields = [
|
||||
CheckboxField::create(
|
||||
$name . '[autoplay]',
|
||||
_t(self::CLASS.'AUTOPLAY', 'Autoplay video?')
|
||||
)->setValue($this->object->getField('Autoplay')),
|
||||
|
||||
CheckboxField::create(
|
||||
$name . '[loop]',
|
||||
_t(self::CLASS.'LOOP', 'Loop video?')
|
||||
)->setValue($this->object->getField('Loop')),
|
||||
|
||||
CheckboxField::create(
|
||||
$name.'[controls]',
|
||||
_t(self::CLASS.'CONTROLS', 'Show player controls?')
|
||||
)->setValue($this->object->getField('Controls'))
|
||||
];
|
||||
|
||||
return CompositeField::create(array_merge([
|
||||
LiteralField::create(
|
||||
$name.'Options',
|
||||
parent::FieldHolder($properties)
|
||||
)
|
||||
], $fields));
|
||||
}
|
||||
}
|
52
src/Extensions/EmbedShortcodeProvider.php
Executable file
52
src/Extensions/EmbedShortcodeProvider.php
Executable file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\View\HTML;
|
||||
|
||||
class EmbedShortcodeProvider extends \SilverStripe\View\Shortcodes\EmbedShortcodeProvider
|
||||
{
|
||||
/**
|
||||
* Build video embed tag
|
||||
*
|
||||
* @param array $arguments
|
||||
* @param string $content Raw HTML content
|
||||
* @return string
|
||||
*/
|
||||
protected static function videoEmbed($arguments, $content)
|
||||
{
|
||||
// Ensure outer div has given width (but leave height auto)
|
||||
if (!empty($arguments['width'])) {
|
||||
$arguments['style'] = 'width:' . (int) $arguments['width'] . 'px';
|
||||
//.';height:' . (int) $arguments['height'] . 'px';
|
||||
}
|
||||
|
||||
// Convert caption to <p>
|
||||
if (!empty($arguments['caption'])) {
|
||||
$xmlCaption = Convert::raw2xml($arguments['caption']);
|
||||
$content .= "\n<p class=\"caption\">{$xmlCaption}</p>";
|
||||
}
|
||||
|
||||
// Convert arguments to data-*argument_name*
|
||||
foreach ($arguments as $k => $v) {
|
||||
if($k === 'class' || $k === 'style') {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($arguments[$k]);
|
||||
$arguments['data-'.$k] = $v;
|
||||
}
|
||||
|
||||
$arguments['class'] .= ' embed-youtube embed-responsive embed-responsive-16by9';
|
||||
|
||||
$iframe = strpos($content, 'iframe');
|
||||
if($iframe >= 0) {
|
||||
$content = substr($content, 0, $iframe+6).' class="embed-responsive-item" '.substr($content, $iframe +7);
|
||||
}
|
||||
|
||||
return HTML::createTag('div', $arguments, $content);
|
||||
}
|
||||
}
|
136
src/Extensions/EmbeddedObjectExtension.php
Executable file
136
src/Extensions/EmbeddedObjectExtension.php
Executable file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class EmbeddedObjectExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'Autoplay' => 'Boolean(0)',
|
||||
'Loop' => 'Boolean(0)',
|
||||
'Controls' => 'Boolean(1)',
|
||||
];
|
||||
|
||||
public function Embed()
|
||||
{
|
||||
$this->owner->Embed();
|
||||
$this->setEmbedParams();
|
||||
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
public function setEmbedParams($params = [])
|
||||
{
|
||||
$iframe_params = [];
|
||||
if ($this->owner->getField('Autoplay')) {
|
||||
$iframe_params[] = 'allow="autoplay"';
|
||||
}
|
||||
|
||||
// YouTube params
|
||||
if (stripos($this->owner->EmbedHTML, 'https://www.youtube.com/embed/') > 0) {
|
||||
$url = $this->owner->getField('SourceURL');
|
||||
preg_match(
|
||||
'/^(?:http(?:s)?:\/\/)?(?:www\.)?(?:m\.)?(?:youtu\.be\/|youtube\.com\/(?:(?:watch)?\?(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)\/))([^\?&"\'>]+)/',
|
||||
$url,
|
||||
$matches
|
||||
);
|
||||
if (isset($matches[1])) {
|
||||
$videoID = $matches[1];
|
||||
|
||||
$params = array_merge($params, [
|
||||
'feature=oembed',
|
||||
'wmode=transparent',
|
||||
'enablejsapi=1',
|
||||
'disablekb=1',
|
||||
'iv_load_policy=3',
|
||||
'modestbranding=1',
|
||||
'rel=0',
|
||||
'showinfo=0',
|
||||
//'controls='.($this->owner->getField('Controls') ? '1': '0')
|
||||
]);
|
||||
|
||||
if ($this->owner->getField('Autoplay')) {
|
||||
$params[] = 'autoplay=1';
|
||||
$params[] = 'mute=1';
|
||||
}
|
||||
|
||||
if ($this->owner->getField('Loop')) {
|
||||
$params[] = 'loop=1';
|
||||
$params[] = 'playlist=' . $videoID;
|
||||
}
|
||||
|
||||
$this->owner->EmbedHTML = preg_replace(
|
||||
'/src="([A-z0-9:\/\.]+)\??(.*?)"/',
|
||||
'src="https://www.youtube.com/embed/' . $videoID . '?' . implode('&', $params) . '" '
|
||||
. implode(' ', $iframe_params),
|
||||
$this->owner->EmbedHTML
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos($this->owner->EmbedHTML, 'https://player.vimeo.com/video/') > 0) {
|
||||
$url = $this->owner->getField('SourceURL');
|
||||
preg_match(
|
||||
'/^https:\/\/vimeo\.com\/([A-z0-9]+)/',
|
||||
$url,
|
||||
$matches
|
||||
);
|
||||
$videoID = $matches[1];
|
||||
|
||||
$params = array_merge($params, [
|
||||
'controls='.($this->owner->getField('Controls') ? '1': '0'),
|
||||
'background=1',
|
||||
]);
|
||||
|
||||
if ($this->owner->getField('Autoplay')) {
|
||||
$params[] = 'autoplay=1';
|
||||
}
|
||||
|
||||
if ($this->owner->getField('Loop')) {
|
||||
$params[] = 'loop=1';
|
||||
}
|
||||
$this->owner->EmbedHTML = preg_replace(
|
||||
'/src="([A-z0-9:\/\.]+)\??(.*?)"/',
|
||||
'src="https://player.vimeo.com/video/'.$videoID.'?' . implode('&', $params) . '" '
|
||||
.implode(' ', $iframe_params),
|
||||
$this->owner->EmbedHTML
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
|
||||
$fields->removeByName([
|
||||
'Width', 'Height', 'EmbedHTML', 'ThumbURL',
|
||||
'Autoplay', 'Loop', 'Controls',
|
||||
'ExtraClass', 'Type',
|
||||
]);
|
||||
|
||||
$fields->addFieldsToTab('Root.Extra', [
|
||||
CheckboxField::create('Autoplay'),
|
||||
CheckboxField::create('Loop'),
|
||||
CheckboxField::create('Controls'),
|
||||
NumericField::create('Width'),
|
||||
NumericField::create('Height'),
|
||||
TextareaField::create('EmbedHTML'),
|
||||
TextField::create('ThumbURL'),
|
||||
TextField::create('ExtraClass'),
|
||||
TextField::create('Type'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
$this->setEmbedParams();
|
||||
}
|
||||
}
|
19
src/Extensions/HtmlEditorFieldExtension.php
Executable file
19
src/Extensions/HtmlEditorFieldExtension.php
Executable file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class HtmlEditorFieldExtension extends DataExtension
|
||||
{
|
||||
public function updateMediaForm(Form $form)
|
||||
{
|
||||
$page_id = $_SESSION['CMSMain']['currentPage'];
|
||||
$page_urlsegment = SiteTree::get()->byID($page_id)->URLSegment;
|
||||
|
||||
$computerUploadField = $form->Fields()->dataFieldByName('AssetUploadField');
|
||||
$computerUploadField->setFolderName(sprintf("%s/images/%s", 'Uploads', $page_urlsegment));
|
||||
}
|
||||
}
|
23
src/Extensions/ImageExtension.php
Executable file
23
src/Extensions/ImageExtension.php
Executable file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class ImageExtension extends DataExtension
|
||||
{
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
|
||||
/*$fields->removeByName([
|
||||
'Filename',
|
||||
]);*/
|
||||
}
|
||||
}
|
26
src/Extensions/LeftAndMainExtension.php
Executable file
26
src/Extensions/LeftAndMainExtension.php
Executable file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\FontAwesome\FontAwesomeField;
|
||||
use SilverStripe\View\Requirements;
|
||||
use A2nt\CMSNiceties\Templates\DeferredRequirements;
|
||||
|
||||
class LeftAndMainExtension extends Extension
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$config = Config::inst()->get(DeferredRequirements::class);
|
||||
// App libs
|
||||
if (!$config['nofontawesome']) {
|
||||
$v = !isset($config['fontawesome_version']) || !$config['fontawesome_version']
|
||||
? Config::inst()->get(FontAwesomeField::class, 'version')
|
||||
: $config['fontawesome_version'];
|
||||
|
||||
Requirements::css('//use.fontawesome.com/releases/v'.$v.'/css/all.css');
|
||||
}
|
||||
}
|
||||
}
|
69
src/Extensions/LostPasswordHandlerExtension.php
Executable file
69
src/Extensions/LostPasswordHandlerExtension.php
Executable file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 6/30/18
|
||||
* Time: 11:37 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\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));
|
||||
}
|
||||
}
|
98
src/Extensions/NotificationsExtension.php
Executable file
98
src/Extensions/NotificationsExtension.php
Executable file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use Dynamic\Elements\Blog\Elements\ElementBlogPosts;
|
||||
use Innoweb\Sitemap\Pages\SitemapPage;
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
|
||||
use SilverStripe\Forms\HeaderField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TreeMultiselectField;
|
||||
use BetterBrief\GoogleMapField;
|
||||
use A2nt\CMSNiceties\Models\Holiday;
|
||||
use A2nt\CMSNiceties\Models\Notification;
|
||||
use A2nt\CMSNiceties\Models\OpeningHour;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
|
||||
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
|
||||
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
|
||||
|
||||
class NotificationsExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'ShowNotifications' => 'Boolean(1)',
|
||||
];
|
||||
|
||||
private static $has_many = [
|
||||
'Notifications' => Notification::class,
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$tab = $fields->findOrMakeTab('Root.Notifications');
|
||||
|
||||
if(!$this->owner->exists()) {
|
||||
$tab->push(LiteralField::create(
|
||||
'NotificationsNotice',
|
||||
'<p class="message notice">The object must be saved before notifications can be added</p>'
|
||||
));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$items = $this->owner->Notifications();
|
||||
|
||||
$config = GridFieldConfig::create();
|
||||
$config->addComponents([
|
||||
new GridFieldToolbarHeader(),
|
||||
new GridFieldTitleHeader(),
|
||||
new GridFieldEditableColumns(),
|
||||
new GridFieldAddNewInlineButton('toolbar-header-right'),
|
||||
new GridFieldDetailForm(),
|
||||
new GridFieldEditButton(),
|
||||
new GridFieldDeleteAction(),
|
||||
]);
|
||||
|
||||
$tab->setChildren(FieldList::create(
|
||||
HeaderField::create('NotificationsHeader','Notifications'),
|
||||
LiteralField::create(
|
||||
'CurrentNotifications',
|
||||
'<b>Current:</b>'
|
||||
.$this->owner->renderWith('App\\Objects\\NotificationsList')
|
||||
),
|
||||
CheckboxField::create('ShowNotifications'),
|
||||
GridField::create(
|
||||
'Notifications',
|
||||
'',
|
||||
$items,
|
||||
$config
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function NotificationsToday()
|
||||
{
|
||||
$items = $this->owner->Notifications();
|
||||
$time = time();
|
||||
|
||||
return $items->filter([
|
||||
'DateOn:LessThanOrEqual' => date('Y-m-d', $time),
|
||||
'DateOff:GreaterThanOrEqual' => date('Y-m-d', $time),
|
||||
]);
|
||||
}
|
||||
}
|
189
src/Extensions/OpenningHoursExtension.php
Executable file
189
src/Extensions/OpenningHoursExtension.php
Executable file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use Dynamic\Elements\Blog\Elements\ElementBlogPosts;
|
||||
use Innoweb\Sitemap\Pages\SitemapPage;
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
|
||||
use SilverStripe\Forms\HeaderField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TreeMultiselectField;
|
||||
use BetterBrief\GoogleMapField;
|
||||
use A2nt\CMSNiceties\Models\Holiday;
|
||||
use A2nt\CMSNiceties\Models\OpeningHour;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
|
||||
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
|
||||
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
|
||||
|
||||
class OpenningHoursExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'ShowOpeningHours' => 'Boolean(1)',
|
||||
'OpenningHoursNote' => 'Varchar(255)',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'OpeningHoursPage' => SiteTree::class,
|
||||
];
|
||||
|
||||
private static $has_many = [
|
||||
'OpeningHours' => OpeningHour::class,
|
||||
'Holidays' => Holiday::class,
|
||||
];
|
||||
|
||||
public function HoursLink()
|
||||
{
|
||||
return $this->owner->OpeningHoursPage()->Link();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$tab = $fields->findOrMakeTab('Root.OpeningHours');
|
||||
|
||||
if(!$this->owner->exists()) {
|
||||
$tab->push(LiteralField::create(
|
||||
'OpeningHoursNotice',
|
||||
'<p class="message notice">The object must be saved before opening hours can be added</p>'
|
||||
));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$hours = $this->owner->OpeningHours();
|
||||
|
||||
$config = GridFieldConfig::create();
|
||||
$config->addComponents([
|
||||
new GridFieldToolbarHeader(),
|
||||
new GridFieldTitleHeader(),
|
||||
new GridFieldEditableColumns(),
|
||||
new GridFieldAddNewInlineButton('toolbar-header-right'),
|
||||
new GridFieldDeleteAction(),
|
||||
]);
|
||||
|
||||
$tab->setChildren(FieldList::create(
|
||||
HeaderField::create('OpeningHours','Opening Hours'),
|
||||
LiteralField::create(
|
||||
'CurrentOpeningHour',
|
||||
'<b>Today:</b>'
|
||||
.'<p class="message notice">'
|
||||
.$this->owner->renderWith('App\\Objects\\OpeningHoursList')
|
||||
.'</p>'
|
||||
),
|
||||
CheckboxField::create('ShowOpeningHours'),
|
||||
DropdownField::create(
|
||||
'OpeningHoursPageID',
|
||||
'Opening Hours Page',
|
||||
SiteTree::get()->map()->toArray()
|
||||
),
|
||||
/*TextareaField::create('OpenningHoursNote'),
|
||||
LiteralField::create(
|
||||
'OpeningHoursNote',
|
||||
'<p><b>Please, specify time ranges. For example:</b><br/>'
|
||||
.'Monday 10:00 AM - 2:00 PM<br/>'
|
||||
.'Monday 3:00 PM - 6:00 PM<br/>'
|
||||
.'Tuesday 12:00 AM - 2:00 PM<br/>'
|
||||
.'Tuesday 3:00 PM - 4:00 PM<br/>'
|
||||
.'...<br/>'
|
||||
.'<b>Short day example durring holidays:</b><br/>'
|
||||
.'Monday 12:00 AM - 2:00 PM 12/31/2018 - 01/06/2019'
|
||||
.'</p>'
|
||||
),*/
|
||||
GridField::create(
|
||||
'OpeningHours',
|
||||
'Opening Hours',
|
||||
$hours,
|
||||
$config
|
||||
)
|
||||
));
|
||||
|
||||
$tab = $fields->findOrMakeTab('Root.Holidays');
|
||||
$tab->push(GridField::create(
|
||||
'Holidays',
|
||||
'Holidays',
|
||||
$this->owner->Holidays(),
|
||||
$config
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opening hours
|
||||
*
|
||||
* @return OpeningHour|DataObject|null
|
||||
*/
|
||||
public function OpeningHoursToday()
|
||||
{
|
||||
$hours = $this->owner->OpeningHours();
|
||||
$time = time();
|
||||
|
||||
$today = $hours->filter([
|
||||
'Day' => date('l', $time),
|
||||
'DisplayStart:LessThanOrEqual' => date('Y-m-d', $time),
|
||||
'DisplayEnd:GreaterThanOrEqual' => date('Y-m-d', $time),
|
||||
]);
|
||||
|
||||
return $today->exists() ? $today : $hours->filter([
|
||||
'Day' => date('l', $time),
|
||||
'DisplayStart' => null,
|
||||
'DisplayEnd' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function OpeningHoursJSON()
|
||||
{
|
||||
$hours = $this->owner->OpeningHours();
|
||||
$result = [];
|
||||
foreach ($hours as $hour) {
|
||||
$from = str_replace(':00', '', date('g:i a', strtotime($hour->getField('From'))));
|
||||
$till = str_replace(':00', '', date('g:i a', strtotime($hour->getField('Till'))));
|
||||
|
||||
$result['days'][$hour->getField('Day')][] = [
|
||||
'From' => $from,
|
||||
'Till' => $till,
|
||||
'DisplayStart' => $hour->getField('DisplayStart'),
|
||||
'DisplayEnd' => $hour->getField('DisplayEnd'),
|
||||
];
|
||||
}
|
||||
|
||||
$holidays = $this->owner->Holidays();
|
||||
foreach ($holidays as $holiday) {
|
||||
$result['holidays'][$holiday->getField('Date')] = $holiday->getField('Title');
|
||||
}
|
||||
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
if ($this->owner->exists() && !$this->owner->OpeningHours()->exists()) {
|
||||
$this->createOpeningHours();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the opening hours for each day of the week
|
||||
*/
|
||||
private function createOpeningHours()
|
||||
{
|
||||
$days = OpeningHour::singleton()->dbObject('Day')->enumValues();
|
||||
foreach ($days as $day) {
|
||||
$openingHour = OpeningHour::create();
|
||||
$openingHour->Day = $day;
|
||||
$this->owner->OpeningHours()->add($openingHour);
|
||||
}
|
||||
}
|
||||
}
|
28
src/Extensions/PageFluentExtension.php
Executable file
28
src/Extensions/PageFluentExtension.php
Executable file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldConfigBlogPost;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use TractorCow\Fluent\Model\Locale;
|
||||
|
||||
class PageFluentExtension extends DataExtension
|
||||
{
|
||||
/**
|
||||
* Override default Fluent fallback
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param Locale $locale
|
||||
*/
|
||||
public function updateLocaliseSelect(&$query, $table, $field, Locale $locale)
|
||||
{
|
||||
// disallow elemental data inheritance in the case that published localised page instance already exists
|
||||
if ($field == 'ElementalAreaID' && $this->owner->isPublishedInLocale()) {
|
||||
$query = '"' . $table . '_Localised_' . $locale->getLocale() . '"."' . $field . '"';
|
||||
}
|
||||
}
|
||||
}
|
37
src/Extensions/PlaceholderFormExtension.php
Executable file
37
src/Extensions/PlaceholderFormExtension.php
Executable file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
|
||||
class PlaceholderFormExtension 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
src/Extensions/ShoppingCartControllerExtension.php
Executable file
24
src/Extensions/ShoppingCartControllerExtension.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 6/23/18
|
||||
* Time: 1:23 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class ShoppingCartControllerExtension extends DataExtension
|
||||
{
|
||||
public function updateAddResponse($request, $response, $product, $quantity)
|
||||
{
|
||||
\PageController::setSiteWideMessage('+'.$quantity.' item(s) was added into the cart', 'success', $request);
|
||||
}
|
||||
|
||||
public function updateRemoveResponse($request, $response, $product, $quantity)
|
||||
{
|
||||
\PageController::setSiteWideMessage(''.$quantity.' item(s) was removed from the cart', 'success', $request);
|
||||
}
|
||||
}
|
139
src/Extensions/SiteConfigExtension.php
Executable file
139
src/Extensions/SiteConfigExtension.php
Executable file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use A2nt\SilverStripeMapboxField\MapboxField;
|
||||
use Innoweb\Sitemap\Pages\SitemapPage;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
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
|
||||
{
|
||||
private static $db = [
|
||||
'ExtraCode' => 'Text',
|
||||
'Longitude' => 'Decimal(10, 8)',
|
||||
'Latitude' => 'Decimal(11, 8)',
|
||||
'MapZoom' => 'Int',
|
||||
//'MapAPIKey' => 'Varchar(255)',
|
||||
'Description' => 'Varchar(255)',
|
||||
'Address' => 'Varchar(255)',
|
||||
'Suburb' => 'Varchar(255)',
|
||||
'State' => 'Varchar(255)',
|
||||
'ZipCode' => 'Varchar(6)',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'PrivacyPolicy' => SiteTree::class,
|
||||
'Sitemap' => SiteTree::class,
|
||||
];
|
||||
|
||||
private static $many_many = [
|
||||
'Navigation' => SiteTree::class,
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$img = Image::get()->filter([
|
||||
'ParentID' => 0,
|
||||
'FileFilename' => 'qrcode.png',
|
||||
])->first();
|
||||
if ($img) {
|
||||
$fields->addFieldsToTab('Root.Main', [
|
||||
LiteralField::create('QRCode', '<img src="'.$img->Link().'" alt="QR code" width="200" style="float:left" />'),
|
||||
]);
|
||||
}
|
||||
|
||||
$fields->addFieldsToTab('Root.Main', [
|
||||
TreeMultiselectField::create(
|
||||
'Navigation',
|
||||
'Navigation',
|
||||
SiteTree::class
|
||||
)->setDisableFunction(static function ($el) {
|
||||
return $el->getField('ParentID') !== 0;
|
||||
}),
|
||||
TextareaField::create('Description', 'Website Description'),
|
||||
TextareaField::create('ExtraCode', 'Extra site-wide HTML code'),
|
||||
DropdownField::create(
|
||||
'PrivacyPolicyID',
|
||||
'Privacy Policy Page',
|
||||
SiteTree::get()->map()->toArray()
|
||||
)->setEmptyString('(Select one)'),
|
||||
DropdownField::create(
|
||||
'SitemapID',
|
||||
'Sitemap Page',
|
||||
SitemapPage::get()->map()->toArray()
|
||||
)->setEmptyString('(Select one)'),
|
||||
]);
|
||||
|
||||
$mapTab = $fields->findOrMakeTab('Root.Maps');
|
||||
$mapTab->setTitle('Address / Map');
|
||||
|
||||
$fields->addFieldsToTab('Root.Maps', [
|
||||
TextField::create('Address'),
|
||||
TextField::create('Suburb', 'City'),
|
||||
TextField::create('State'),
|
||||
TextField::create('ZipCode'),
|
||||
]);
|
||||
|
||||
if (MapboxField::getAccessToken()) {
|
||||
$fields->addFieldsToTab('Root.Maps', [
|
||||
//TextField::create('MapAPIKey'),
|
||||
TextField::create('MapZoom'),
|
||||
MapboxField::create('Map', 'Choose a location', 'Latitude', 'Longitude'),
|
||||
]);
|
||||
} else {
|
||||
$fields->addFieldsToTab('Root.Maps', [
|
||||
LiteralField::create('MapNotice', '<p class="alert alert-info">No Map API keys specified.</p>')
|
||||
]);
|
||||
}
|
||||
|
||||
/*GoogleMapField::create(
|
||||
$this->owner,
|
||||
'Location',
|
||||
[
|
||||
'show_search_box' => true,
|
||||
]
|
||||
)*/
|
||||
}
|
||||
|
||||
public function MapAPIKey()
|
||||
{
|
||||
return MapboxField::config()->get('access_token');
|
||||
}
|
||||
|
||||
public function MapStyle()
|
||||
{
|
||||
return MapboxField::config()->get('map_style');
|
||||
}
|
||||
|
||||
public function getGeoJSON()
|
||||
{
|
||||
return '{"type": "MarkerCollection","features": [{"type": "Feature","icon": "<i class=\'fas fa-map-marker-alt\'></i>",'
|
||||
.'"properties": {"content": "'.$this->owner->getTitle().'"},"geometry": {"type": "Point",'
|
||||
.'"coordinates": ['.$this->owner->getField('Longitude').','.$this->owner->getField('Latitude').']}}]}';
|
||||
}
|
||||
|
||||
public function DirectionsLink()
|
||||
{
|
||||
return '<a href="https://www.google.com/maps/dir/Current+Location/'
|
||||
.$this->owner->getField('Latitude').','
|
||||
.$this->owner->getField('Longitude').'" class="btn btn-primary btn-directions" target="_blank">'
|
||||
.'<i class="fas fa-road"></i> Get Directions</a>';
|
||||
}
|
||||
|
||||
public function getLatestBlogPosts()
|
||||
{
|
||||
return BlogPost::get()->sort('PublishDate DESC');
|
||||
}
|
||||
}
|
51
src/Extensions/SiteMemberLoginForm.php
Normal file
51
src/Extensions/SiteMemberLoginForm.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Security\MemberAuthenticator\MemberLoginForm;
|
||||
|
||||
class SiteMemberLoginForm extends MemberLoginForm
|
||||
{
|
||||
public function __construct(
|
||||
$controller,
|
||||
$authenticatorClass,
|
||||
$name,
|
||||
$fields = null,
|
||||
$actions = null,
|
||||
$checkCurrentUser = true
|
||||
) {
|
||||
parent::__construct($controller, $authenticatorClass, $name, $fields, $actions, $checkCurrentUser);
|
||||
|
||||
$fields = $this->Fields();
|
||||
$actions = $this->Actions();
|
||||
|
||||
$email = $fields->fieldByName('Email');
|
||||
if ($email) {
|
||||
$email
|
||||
->setAttribute('placeholder', 'your@email.com')
|
||||
->setAttribute('autocomplete', 'email')
|
||||
->setAttribute('type', 'email');
|
||||
}
|
||||
|
||||
$pass = $fields->fieldByName('Password');
|
||||
if($pass) {
|
||||
//$pass->setAttribute('autocomplete', 'current-password');
|
||||
$pass->setAttribute('placeholder', '**********');
|
||||
$pass->setAutofocus(true);
|
||||
}
|
||||
|
||||
$btn = $actions->fieldByName('action_doLogin');
|
||||
if($btn) {
|
||||
$btn->setUseButtonTag(true);
|
||||
$btn->setButtonContent('<i class="fas fa-check"></i> '.$btn->Title());
|
||||
$btn->addExtraClass('btn-lg');
|
||||
}
|
||||
|
||||
if (Director::isLive()) {
|
||||
$this->enableSpamProtection();
|
||||
}
|
||||
}
|
||||
}
|
24
src/Extensions/SiteTreeExtension.php
Executable file
24
src/Extensions/SiteTreeExtension.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
|
||||
class SiteTreeExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'ExtraCode' => 'Text',
|
||||
];
|
||||
|
||||
public function updateSettingsFields(FieldList $fields)
|
||||
{
|
||||
$fields->addFieldsToTab('Root.Settings', [
|
||||
TextareaField::create(
|
||||
'ExtraCode',
|
||||
'Extra page specific HTML code'
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
74
src/Extensions/SlideImageExtension.php
Executable file
74
src/Extensions/SlideImageExtension.php
Executable file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 6/30/18
|
||||
* Time: 11:37 PM
|
||||
* Ref: Dynamic\FlexSlider\Model\SlideImage
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\DatetimeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\ToggleCompositeField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class SlideImageExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'Hide' => 'Boolean(0)',
|
||||
'DateOn' => 'Datetime',
|
||||
'DateOff' => 'Datetime',
|
||||
];
|
||||
|
||||
private $_cache = [];
|
||||
|
||||
public function getElement()
|
||||
{
|
||||
if(!isset($this->_cache['element'])) {
|
||||
$this->_cache['element'] = $this->owner->SlideshowElement();
|
||||
}
|
||||
|
||||
return $this->_cache['element'];
|
||||
}
|
||||
|
||||
public function getSlideWidth()
|
||||
{
|
||||
$element = $this->getElement();
|
||||
return $element->getSlideWidth();
|
||||
}
|
||||
|
||||
public function getSlideHeight()
|
||||
{
|
||||
$element = $this->getElement();
|
||||
return $element->getSlideHeight();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
|
||||
$fields->removeByName([
|
||||
'PageLinkID',
|
||||
'Hide',
|
||||
'DateOn',
|
||||
'DateOff',
|
||||
]);
|
||||
|
||||
|
||||
$fields->dataFieldByName('Image')
|
||||
->setTitle('Image ('.$this->getSlideWidth().' x '.$this->getSlideHeight().' px)');
|
||||
|
||||
$fields->addFieldToTab('Root.Main', ToggleCompositeField::create(
|
||||
'ConfigHD',
|
||||
'Slide Settings',
|
||||
[
|
||||
CheckboxField::create('Hide', 'Hide this slide? (That will hide the slide regardless of start/end fields)'),
|
||||
DatetimeField::create('DateOn', 'When would you like to start showing the slide?'),
|
||||
DatetimeField::create('DateOff', 'When would you like to stop showing the slide?'),
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
75
src/Extensions/SocialExtension.php
Executable file
75
src/Extensions/SocialExtension.php
Executable file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 6/30/18
|
||||
* Time: 11:37 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class SocialExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
//'PhoneNumber' => 'Varchar(255)',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Facebook' => Link::class,
|
||||
'LinkedIn' => Link::class,
|
||||
'Pinterest' => Link::class,
|
||||
'Instagram' => Link::class,
|
||||
'Twitter' => Link::class,
|
||||
'PublicEmail' => Link::class,
|
||||
'PhoneNumber' => Link::class,
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
|
||||
$linkFields = [
|
||||
LinkField::create('FacebookID'),
|
||||
LinkField::create('LinkedInID'),
|
||||
LinkField::create('PinterestID'),
|
||||
LinkField::create('InstagramID'),
|
||||
LinkField::create('TwitterID'),
|
||||
];
|
||||
|
||||
foreach ($linkFields as $field) {
|
||||
$field->setAllowedTypes(['URL']);
|
||||
}
|
||||
|
||||
$fields->findOrMakeTab('Root.Social');
|
||||
|
||||
$fields->addFieldsToTab('Root.Social', [
|
||||
LinkField::create('PublicEmailID', 'Public Email')
|
||||
->setAllowedTypes(['Email']),
|
||||
LinkField::create('PhoneNumberID', 'Phone Number')
|
||||
->setAllowedTypes(['Phone']),
|
||||
]);
|
||||
|
||||
$fields->addFieldsToTab('Root.Social', $linkFields);
|
||||
}
|
||||
|
||||
public static function byPhone($phone)
|
||||
{
|
||||
$links = Link::get()->filter('Phone', $phone);
|
||||
|
||||
if ($links->exists()) {
|
||||
return Member::get()->filter(
|
||||
'PhoneNumberID',
|
||||
array_keys($links->map('ID', 'Title')->toArray())
|
||||
)->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
10
src/Extensions/UserDefinedForm_HiddenClass.php
Normal file
10
src/Extensions/UserDefinedForm_HiddenClass.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace A2nt\CMSNiceties\Extensions;
|
||||
|
||||
use SilverStripe\ORM\HiddenClass;
|
||||
use SilverStripe\UserForms\Model\UserDefinedForm;
|
||||
|
||||
class UserDefinedForm_HiddenClass extends UserDefinedForm implements HiddenClass
|
||||
{
|
||||
}
|
140
src/Forms/GridField/SaveAllButton.php
Normal file
140
src/Forms/GridField/SaveAllButton.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php namespace A2nt\CMSNiceties\Forms\GridField;
|
||||
|
||||
/**
|
||||
* Milkyway Multimedia
|
||||
* SaveAllButton.php
|
||||
*
|
||||
* @package milkyway-multimedia/ss-gridfield-utils
|
||||
* @author Mellisa Hankins <mell@milkywaymultimedia.com.au>
|
||||
*/
|
||||
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_ActionProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||
use SilverStripe\Forms\GridField\GridField_SaveHandler;
|
||||
use SilverStripe\Control\Controller;
|
||||
|
||||
class SaveAllButton implements GridField_HTMLProvider, GridField_ActionProvider
|
||||
{
|
||||
protected $targetFragment;
|
||||
protected $actionName = 'saveallrecords';
|
||||
|
||||
public $buttonName;
|
||||
|
||||
public $publish = true;
|
||||
|
||||
public $completeMessage;
|
||||
|
||||
public $removeChangeFlagOnFormOnSave = false;
|
||||
|
||||
public function setButtonName($name)
|
||||
{
|
||||
$this->buttonName = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRemoveChangeFlagOnFormOnSave($flag)
|
||||
{
|
||||
$this->removeChangeFlagOnFormOnSave = $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __construct($targetFragment = 'before', $publish = true, $action = 'saveallrecords')
|
||||
{
|
||||
$this->targetFragment = $targetFragment;
|
||||
$this->publish = $publish;
|
||||
$this->actionName = $action;
|
||||
}
|
||||
|
||||
public function getHTMLFragments($gridField)
|
||||
{
|
||||
$singleton = singleton($gridField->getModelClass());
|
||||
|
||||
if (!$singleton->canEdit() && !$singleton->canCreate()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!$this->buttonName) {
|
||||
if ($this->publish && $singleton->hasExtension('Versioned')) {
|
||||
$this->buttonName = _t('GridField.SAVE_ALL_AND_PUBLISH', 'Save all and publish');
|
||||
} else {
|
||||
$this->buttonName = _t('GridField.SAVE_ALL', 'Save all');
|
||||
}
|
||||
}
|
||||
|
||||
$button = GridField_FormAction::create(
|
||||
$gridField,
|
||||
$this->actionName,
|
||||
$this->buttonName,
|
||||
$this->actionName,
|
||||
null
|
||||
);
|
||||
|
||||
$button->setAttribute('data-icon', 'disk')->addExtraClass('new new-link ui-button-text-icon-primary');
|
||||
|
||||
if ($this->removeChangeFlagOnFormOnSave) {
|
||||
$button->addExtraClass('js-mwm-gridfield--saveall');
|
||||
}
|
||||
|
||||
return [
|
||||
$this->targetFragment => $button->Field(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getActions($gridField)
|
||||
{
|
||||
return [$this->actionName];
|
||||
}
|
||||
|
||||
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
|
||||
{
|
||||
if ($actionName == $this->actionName) {
|
||||
return $this->saveAllRecords($gridField, $arguments, $data);
|
||||
}
|
||||
}
|
||||
|
||||
protected function saveAllRecords(GridField $grid, $arguments, $data)
|
||||
{
|
||||
if (isset($data[$grid->Name])) {
|
||||
$currValue = $grid->Value();
|
||||
$grid->setValue($data[$grid->Name]);
|
||||
$model = singleton($grid->List->dataClass());
|
||||
|
||||
foreach ($grid->getConfig()->getComponents() as $component) {
|
||||
if ($component instanceof GridField_SaveHandler) {
|
||||
$component->handleSave($grid, $model);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->publish) {
|
||||
// Only use the viewable list items, since bulk publishing can take a toll on the system
|
||||
$list = ($paginator = $grid->getConfig()->getComponentByType('GridFieldPaginator')) ? $paginator->getManipulatedData($grid, $grid->List) : $grid->List;
|
||||
|
||||
$list->each(
|
||||
function ($item) {
|
||||
if ($item->hasExtension('Versioned')) {
|
||||
$item->writeToStage('Stage');
|
||||
$item->publish('Stage', 'Live');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ($model->exists()) {
|
||||
$model->delete();
|
||||
$model->destroy();
|
||||
}
|
||||
|
||||
$grid->setValue($currValue);
|
||||
|
||||
if (Controller::curr() && $response = Controller::curr()->Response) {
|
||||
if (!$this->completeMessage) {
|
||||
$this->completeMessage = _t('GridField.DONE', 'Done.');
|
||||
}
|
||||
|
||||
$response->addHeader('X-Status', rawurlencode($this->completeMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
src/GraphQL/APIKeyAuthenticator.php
Normal file
46
src/GraphQL/APIKeyAuthenticator.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\GraphQL\Auth\AuthenticatorInterface;
|
||||
use SilverStripe\ORM\ValidationException;
|
||||
use SilverStripe\Security\Member;
|
||||
use A2nt\CMSNiceties\Templates\WebpackTemplateProvider;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
class APIKeyAuthenticator implements AuthenticatorInterface
|
||||
{
|
||||
public function authenticate(HTTPRequest $request)
|
||||
{
|
||||
$member = Security::getCurrentUser();
|
||||
|
||||
if (Director::isLive()
|
||||
&& $request->getHeader('apikey') !== WebpackTemplateProvider::config()['GRAPHQL_API_KEY']
|
||||
) {
|
||||
if ($member && Permission::checkMember($member, 'CMS_ACCESS')) {
|
||||
return $member;
|
||||
}
|
||||
|
||||
throw new ValidationException('Restricted resource', 401);
|
||||
}
|
||||
|
||||
return Member::get()->first();
|
||||
}
|
||||
|
||||
public function isApplicable(HTTPRequest $request)
|
||||
{
|
||||
if ($request->param('Controller') === '%$SilverStripe\GraphQL\Controller.admin') {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*if($request->getHeader('apikey')){
|
||||
return true;
|
||||
}*/
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
23
src/GraphQL/APIKeyMiddleware.php
Normal file
23
src/GraphQL/APIKeyMiddleware.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Schema;
|
||||
use SilverStripe\GraphQL\Middleware\QueryMiddleware;
|
||||
use A2nt\CMSNiceties\Templates\WebpackTemplateProvider;
|
||||
|
||||
class APIKeyMiddleware implements QueryMiddleware
|
||||
{
|
||||
public function process(Schema $schema, $query, $context, $params, callable $next)
|
||||
{
|
||||
var_dump($context);
|
||||
die('saaddsdsads');
|
||||
if($request->getHeader('apikey') === WebpackTemplateProvider::config()['GRAPHQL_API_KEY']) {
|
||||
return $next($schema, $query, $context, $params);
|
||||
}
|
||||
|
||||
throw new \Exception('Invalid API key token');
|
||||
}
|
||||
}
|
37
src/GraphQL/ElementTypeCreator.php
Normal file
37
src/GraphQL/ElementTypeCreator.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\GraphQL\TypeCreator;
|
||||
|
||||
class ElementTypeCreator extends TypeCreator
|
||||
{
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'name' => 'element'
|
||||
];
|
||||
}
|
||||
|
||||
public function fields()
|
||||
{
|
||||
return [
|
||||
'_id' => ['type' => Type::nonNull(Type::id()),'resolve' => static function($object) {
|
||||
return $object->ID;
|
||||
}],
|
||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
||||
'Title' => ['type' => Type::string()],
|
||||
'ParentID' => ['type' => Type::id()],
|
||||
'Render' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => static function($object, array $args, $context, ResolveInfo $info) {
|
||||
return $object->getController()->forTemplate()->HTML();
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
29
src/GraphQL/MemberTypeCreator.php
Normal file
29
src/GraphQL/MemberTypeCreator.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\GraphQL\TypeCreator;
|
||||
use SilverStripe\GraphQL\Pagination\Connection;
|
||||
|
||||
class MemberTypeCreator extends TypeCreator
|
||||
{
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'name' => 'member'
|
||||
];
|
||||
}
|
||||
|
||||
public function fields()
|
||||
{
|
||||
return [
|
||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
||||
'Email' => ['type' => Type::string()],
|
||||
'FirstName' => ['type' => Type::string()],
|
||||
'Surname' => ['type' => Type::string()],
|
||||
];
|
||||
}
|
||||
}
|
122
src/GraphQL/PageTypeCreator.php
Normal file
122
src/GraphQL/PageTypeCreator.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\CMS\Controllers\ModelAsController;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\GraphQL\TypeCreator;
|
||||
use SilverStripe\GraphQL\Pagination\Connection;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
class PageTypeCreator extends TypeCreator
|
||||
{
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'name' => 'page'
|
||||
];
|
||||
}
|
||||
|
||||
public function fields()
|
||||
{
|
||||
$elementsConnection = Connection::create('Elements')
|
||||
->setConnectionType($this->manager->getType('element'))
|
||||
->setDescription('A list of the page elements')
|
||||
->setSortableFields(['ID', 'Title']);
|
||||
|
||||
return [
|
||||
'_id' => ['type' => Type::nonNull(Type::id()),'resolve' => static function($object) {
|
||||
return $object->ID;
|
||||
}],
|
||||
'ID' => ['type' => Type::nonNull(Type::id())],
|
||||
'Title' => ['type' => Type::string()],
|
||||
'Content' => ['type' => Type::string()],
|
||||
'Link' => ['type' => Type::string(), 'resolve' => static function($object) {
|
||||
return $object->Link();
|
||||
}],
|
||||
'URLSegment' => ['type' => Type::string()],
|
||||
'ParentID' => ['type' => Type::id()],
|
||||
'ClassName' => ['type' => Type::string()],
|
||||
'CSSClass' => ['type' => Type::string(), 'resolve' => static function($object) {
|
||||
return $object->CSSClass();
|
||||
}],
|
||||
'Summary' => ['type' => Type::string(), 'resolve' => static function($object) {
|
||||
return $object->Summary();
|
||||
}],
|
||||
'HTML' => ['type' => Type::string(), 'resolve' => static function($object) {
|
||||
// get action from request
|
||||
$action = null;
|
||||
|
||||
/** @var \Page $object */
|
||||
Director::set_current_page($object);
|
||||
/** @var \PageController $controller */
|
||||
$controller = ModelAsController::controller_for($object);
|
||||
|
||||
// find templates
|
||||
$tpl = 'Page';
|
||||
$tpls = SSViewer::get_templates_by_class(
|
||||
$object->ClassName,
|
||||
($action ? '_'.$action : ''),
|
||||
\Page::class
|
||||
);
|
||||
|
||||
foreach ($tpls as $tpl){
|
||||
if(is_array($tpl)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$a_tpl = explode('\\',$tpl);
|
||||
$last_name = array_pop($a_tpl);
|
||||
$a_tpl[] = 'Layout';
|
||||
$a_tpl[] = $last_name;
|
||||
$a_tpl = implode('\\', $a_tpl);
|
||||
|
||||
if(SSViewer::hasTemplate($a_tpl)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
$tpl = ($tpl !== 'Page') ? $tpl : 'Layout/Page';
|
||||
|
||||
$action = $action ? $action : 'index';
|
||||
/** @var HTTPRequest $request */
|
||||
$request = new HTTPRequest('GET', $object->AbsoluteLink());
|
||||
$request->setSession(new Session([]));
|
||||
|
||||
// a little dirty way to make forms working
|
||||
Controller::curr()->config()->set('url_segment', $object->AbsoluteLink());
|
||||
/*$controller->setRequest($request);*/
|
||||
//$request->getSession()->init($request);
|
||||
|
||||
$controller->setRequest($request);
|
||||
$controller->setAction($action);
|
||||
//$controller->pushCurrent();
|
||||
$controller->doInit();
|
||||
|
||||
$layout = $controller->renderWith($tpl);
|
||||
return $controller
|
||||
->customise(['Layout' => $layout])
|
||||
->renderWith('GraphQLPage')->HTML();
|
||||
}],
|
||||
'Elements' => [
|
||||
'type' => $elementsConnection->toType(),
|
||||
'args' => $elementsConnection->args(),
|
||||
'resolve' => static function($object, array $args, $context, ResolveInfo $info) use ($elementsConnection) {
|
||||
return $elementsConnection->resolveList(
|
||||
$object->ElementalArea()->Elements(),
|
||||
$args,
|
||||
$context
|
||||
);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
44
src/GraphQL/PaginatedReadMembersQueryCreator.php
Normal file
44
src/GraphQL/PaginatedReadMembersQueryCreator.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\GraphQL\Pagination\Connection;
|
||||
use SilverStripe\GraphQL\Pagination\PaginatedQueryCreator;
|
||||
|
||||
class PaginatedReadMembersQueryCreator extends PaginatedQueryCreator
|
||||
{
|
||||
public function createConnection()
|
||||
{
|
||||
|
||||
return Connection::create('paginatedReadMembers')
|
||||
->setConnectionType($this->manager->getType('member'))
|
||||
->setArgs([
|
||||
'Email' => [
|
||||
'type' => Type::string()
|
||||
]
|
||||
])
|
||||
->setSortableFields(['ID', 'FirstName', 'Email'])
|
||||
->setConnectionResolver(static function ($object, array $args, $context, ResolveInfo $info) {
|
||||
$member = Member::singleton();
|
||||
if (!$member->canView($context['currentUser'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'%s view access not permitted',
|
||||
Member::class
|
||||
));
|
||||
}
|
||||
$list = Member::get();
|
||||
|
||||
// Optional filtering by properties
|
||||
if (isset($args['Email'])) {
|
||||
$list = $list->filter('Email', $args['Email']);
|
||||
}
|
||||
|
||||
return $list;
|
||||
});
|
||||
}
|
||||
}
|
59
src/GraphQL/PaginatedReadPagesQueryCreator.php
Normal file
59
src/GraphQL/PaginatedReadPagesQueryCreator.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\GraphQL\Pagination\Connection;
|
||||
use SilverStripe\GraphQL\Pagination\PaginatedQueryCreator;
|
||||
|
||||
class PaginatedReadPagesQueryCreator extends PaginatedQueryCreator
|
||||
{
|
||||
public function createConnection()
|
||||
{
|
||||
|
||||
return Connection::create('readPages')
|
||||
->setConnectionType($this->manager->getType('page'))
|
||||
->setArgs([
|
||||
'Link' => [
|
||||
'type' => Type::string()
|
||||
]
|
||||
])
|
||||
->setSortableFields(['Sort'])
|
||||
->setConnectionResolver(static function ($object, array $args, $context, ResolveInfo $info) {
|
||||
|
||||
if (isset($args['Link'])) {
|
||||
$link = $args['Link'];
|
||||
|
||||
if(SiteTree::has_extension('\TractorCow\Fluent\Extension\FluentSiteTreeExtension')) {
|
||||
$arr = array_filter(explode('/', $args['Link']));
|
||||
|
||||
$locale = \TractorCow\Fluent\Model\Locale::get()->filter('URLSegment', array_shift($arr))->first();
|
||||
\TractorCow\Fluent\State\FluentState::singleton()->setLocale($locale->Locale);
|
||||
|
||||
$link = implode('/', $arr);
|
||||
}
|
||||
|
||||
|
||||
$list = ArrayList::create();
|
||||
$page = SiteTree::get_by_link($link);
|
||||
$list->add($page);
|
||||
}
|
||||
|
||||
/*$list = \Page::get();
|
||||
|
||||
// Optional filtering by properties
|
||||
if (isset($args['ID'])) {
|
||||
$list = $list->filter('ID', $args['ID']);
|
||||
}*/
|
||||
|
||||
return $list;
|
||||
});
|
||||
}
|
||||
}
|
53
src/GraphQL/ReadMembersQueryCreator.php
Normal file
53
src/GraphQL/ReadMembersQueryCreator.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\GraphQL;
|
||||
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\GraphQL\OperationResolver;
|
||||
use SilverStripe\GraphQL\QueryCreator;
|
||||
|
||||
class ReadMembersQueryCreator extends QueryCreator implements OperationResolver
|
||||
{
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'name' => 'readMembers'
|
||||
];
|
||||
}
|
||||
|
||||
public function args()
|
||||
{
|
||||
return [
|
||||
'Email' => ['type' => Type::string()]
|
||||
];
|
||||
}
|
||||
|
||||
public function type()
|
||||
{
|
||||
return Type::listOf($this->manager->getType('member'));
|
||||
}
|
||||
|
||||
public function resolve($object, array $args, $context, ResolveInfo $info)
|
||||
{
|
||||
$member = Member::singleton();
|
||||
|
||||
if (!$member->canView($context['currentUser'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'%s view access not permitted',
|
||||
Member::class
|
||||
));
|
||||
}
|
||||
$list = Member::get();
|
||||
|
||||
// Optional filtering by properties
|
||||
if (isset($args['Email'])) {
|
||||
$list = $list->filter('Email', $args['Email']);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
57
src/Models/Holiday.php
Executable file
57
src/Models/Holiday.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 9/12/18
|
||||
* Time: 2:55 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Models;
|
||||
|
||||
use Dynamic\FlexSlider\Model\SlideImage;
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\ValidationResult;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
|
||||
class Holiday extends DataObject
|
||||
{
|
||||
private static $table_name = 'Holiday';
|
||||
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(255)',
|
||||
'Date' => 'Date',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Parent' => SiteConfig::class,
|
||||
];
|
||||
|
||||
|
||||
private static $summary_fields = [
|
||||
'Title' => 'Title',
|
||||
'Date' => 'Date',
|
||||
];
|
||||
|
||||
private static $default_sort = 'Date ASC, Title ASC';
|
||||
|
||||
public function validate()
|
||||
{
|
||||
$result = parent::validate();
|
||||
|
||||
$exists = self::get()->filter([
|
||||
'ID:not' => $this->ID,
|
||||
'Date' => $this->getField('Date'),
|
||||
])->exists();
|
||||
|
||||
if($exists) {
|
||||
return $result->addError(
|
||||
'Holiday was defined already.',
|
||||
ValidationResult::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
80
src/Models/Notification.php
Executable file
80
src/Models/Notification.php
Executable file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 9/12/18
|
||||
* Time: 2:55 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Models;
|
||||
|
||||
use Dynamic\FlexSlider\Model\SlideImage;
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\ValidationResult;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
|
||||
class Notification extends DataObject
|
||||
{
|
||||
private static $table_name = 'Notification';
|
||||
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(255)',
|
||||
'Content' => 'Text',
|
||||
'DateOn' => 'Date',
|
||||
'DateOff' => 'Date',
|
||||
'Area' => 'Enum("Site","Site")',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Parent' => SiteConfig::class,
|
||||
'TargetLink' => Link::class,
|
||||
];
|
||||
|
||||
private static $defaults = [
|
||||
'Area' => 'Site',
|
||||
];
|
||||
|
||||
|
||||
private static $summary_fields = [
|
||||
'Title' => 'Title',
|
||||
'Content' => 'Text',
|
||||
'DateOn' => 'Turn on date',
|
||||
'DateOff' => 'Turn off date',
|
||||
];
|
||||
|
||||
private static $default_sort = 'DateOn DESC, DateOff DESC, Title ASC';
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->addFieldsToTab('Root.Main', [
|
||||
LinkField::create('TargetLinkID', 'Link'),
|
||||
]);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function validate()
|
||||
{
|
||||
$result = parent::validate();
|
||||
|
||||
if (!$this->getField('DateOn') || !$this->getField('DateOff')) {
|
||||
return $result->addError(
|
||||
'Turn on and turn off dates are required.',
|
||||
ValidationResult::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->getField('Content')) {
|
||||
return $result->addError(
|
||||
'Text field required.',
|
||||
ValidationResult::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
90
src/Models/OpeningHour.php
Executable file
90
src/Models/OpeningHour.php
Executable file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 9/12/18
|
||||
* Time: 2:55 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Models;
|
||||
|
||||
use Dynamic\FlexSlider\Model\SlideImage;
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\ValidationResult;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
|
||||
class OpeningHour extends DataObject
|
||||
{
|
||||
private static $table_name = 'OpeningHour';
|
||||
|
||||
private static $db = [
|
||||
'Day' => 'Enum("Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday", "Monday")',
|
||||
'From' => 'Time',
|
||||
'Till' => 'Time',
|
||||
'Comment' => 'Varchar(255)',
|
||||
'DisplayStart' => 'Date',
|
||||
'DisplayEnd' => 'Date',
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'Parent' => SiteConfig::class,
|
||||
];
|
||||
|
||||
private static $defaults = [
|
||||
'Day' => 'Monday',
|
||||
'From' => '09:00:00',
|
||||
'Till' => '22:00:00',
|
||||
];
|
||||
|
||||
private static $summary_fields = [
|
||||
'Day' => 'Day *',
|
||||
'From' => 'From *',
|
||||
'Till' => 'Till *',
|
||||
//'Comment' => 'Comment',
|
||||
'DisplayStart' => 'Display Start',
|
||||
'DisplayEnd' => 'Display End',
|
||||
];
|
||||
|
||||
private static $default_sort = 'Day ASC, From ASC';
|
||||
|
||||
public function validate()
|
||||
{
|
||||
$result = parent::validate();
|
||||
|
||||
|
||||
if (!$this->getField('Day')
|
||||
|| !$this->getField('From')
|
||||
|| !$this->getField('Till')
|
||||
|| $this->getField('From') > $this->getField('Till')
|
||||
) {
|
||||
return $result->addError(
|
||||
'Day, From and Till fields are required or wrong time range was specified.',
|
||||
ValidationResult::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
$exists = self::get()->filter([
|
||||
'ID:not' => $this->ID,
|
||||
'ParentID' => $this->getField('ParentID'),
|
||||
'Day' => $this->getField('Day'),
|
||||
'From:GreaterThanOrEqual' => $this->getField('From'),
|
||||
'Till:LessThanOrEqual' => $this->getField('Till'),
|
||||
])->exists();
|
||||
|
||||
if ($exists) {
|
||||
return $result->addError(
|
||||
'Hours were defined already for specified range.',
|
||||
ValidationResult::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function forTemplate()
|
||||
{
|
||||
return $this->renderWith(__CLASS__);
|
||||
}
|
||||
}
|
47
src/Shop/CheckoutMapComponent.php
Executable file
47
src/Shop/CheckoutMapComponent.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 8/26/18
|
||||
* Time: 1:40 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Models;
|
||||
|
||||
|
||||
use SilverShop\Checkout\Component\CheckoutComponent;
|
||||
use SilverShop\Model\Order;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\HeaderField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
class CheckoutMapComponent extends CheckoutComponent
|
||||
{
|
||||
public function getFormFields(Order $order)
|
||||
{
|
||||
$config = SiteConfig::current_site_config();
|
||||
|
||||
return FieldList::create(
|
||||
HeaderField::create('MapHeader', 'Pick up location'),
|
||||
LiteralField::create(
|
||||
'Map',
|
||||
SSViewer::create('Objects\Map')->process($config)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function validateData(Order $order, array $data)
|
||||
{
|
||||
}
|
||||
|
||||
public function setData(Order $order, array $data)
|
||||
{
|
||||
}
|
||||
|
||||
public function getData(Order $order)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
43
src/Shop/CheckoutNoDeliveryConfig.php
Executable file
43
src/Shop/CheckoutNoDeliveryConfig.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 8/26/18
|
||||
* Time: 1:08 PM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Models;
|
||||
|
||||
|
||||
use SilverShop\Checkout\Checkout;
|
||||
use SilverShop\Checkout\CheckoutComponentConfig;
|
||||
use SilverShop\Checkout\Component\CustomerDetails;
|
||||
use SilverShop\Checkout\Component\Notes;
|
||||
use SilverShop\Checkout\Component\Payment;
|
||||
use SilverShop\Checkout\Component\Terms;
|
||||
use SilverShop\Checkout\Component\Membership;
|
||||
use SilverShop\Model\Order;
|
||||
use SilverStripe\Omnipay\GatewayInfo;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
class CheckoutNoDeliveryConfig extends CheckoutComponentConfig
|
||||
{
|
||||
public function __construct(Order $order)
|
||||
{
|
||||
parent::__construct($order);
|
||||
$this->addComponent(CustomerDetails::create());
|
||||
|
||||
if (Checkout::member_creation_enabled() && !Security::getCurrentUser()) {
|
||||
$this->addComponent(Membership::create());
|
||||
}
|
||||
|
||||
if (count(GatewayInfo::getSupportedGateways()) > 1) {
|
||||
$this->addComponent(Payment::create());
|
||||
}
|
||||
|
||||
$this->addComponent(Notes::create());
|
||||
|
||||
$this->addComponent(CheckoutMapComponent::create());
|
||||
$this->addComponent(Terms::create());
|
||||
}
|
||||
}
|
1
src/Shop/_manifest_exclude
Executable file
1
src/Shop/_manifest_exclude
Executable file
@ -0,0 +1 @@
|
||||
Remove it in case u need silvershop module
|
39
src/Tasks/BrokenFilesTask.php
Executable file
39
src/Tasks/BrokenFilesTask.php
Executable file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Tasks;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
|
||||
class BrokenFilesTask extends BuildTask
|
||||
{
|
||||
protected $title = 'Broken Files Task';
|
||||
|
||||
protected $description = 'Broken files report';
|
||||
|
||||
protected $enabled = true;
|
||||
|
||||
public function run($request)
|
||||
{
|
||||
$files = File::get();
|
||||
$i = 0;
|
||||
foreach ($files as $file) {
|
||||
if (!$file->exists()) {
|
||||
echo '<b style="color:red">File name was not found at SS DB: '
|
||||
.$file->getField('Name').'</b><br/>'
|
||||
.PHP_EOL;
|
||||
|
||||
$i++;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
echo ($i > 0) ?
|
||||
'<h2 style="color:red">Missing '.$i.' files</h2>'
|
||||
: '<h2 style="color:green">All files are ok!</h2>';
|
||||
|
||||
die('Done!');
|
||||
}
|
||||
}
|
78
src/Tasks/BuildTask.php
Executable file
78
src/Tasks/BuildTask.php
Executable file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Tasks;
|
||||
|
||||
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
|
||||
class BuildTask extends \SilverStripe\Dev\BuildTask
|
||||
{
|
||||
|
||||
protected $title = 'Base build task interface';
|
||||
protected $description = 'Base build task interface';
|
||||
protected $enabled = false;
|
||||
|
||||
protected $messages = [];
|
||||
/**
|
||||
* Implement this method in the task subclass to
|
||||
* execute via the TaskRunner
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @return
|
||||
*/
|
||||
public function run($request)
|
||||
{
|
||||
// TODO: Implement run() method.
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
public function Title()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
protected function setMessage($msg, $type = 'msg')
|
||||
{
|
||||
if(is_array($msg)) {
|
||||
$type = 'list';
|
||||
}
|
||||
|
||||
$this->messages[] = [$type, $msg];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
echo '<style>'
|
||||
.'.info{color:#053bff}'
|
||||
.'.bad,.error{color:red}'
|
||||
.'.good,.success{color:green}'
|
||||
.'</style>';
|
||||
|
||||
foreach ($this->messages as $item) {
|
||||
$type = $item[0];
|
||||
$msg = $item[1];
|
||||
|
||||
switch ($type) {
|
||||
case 'h2':
|
||||
echo '<h2>'.$msg.'</h2>'.PHP_EOL;
|
||||
break;
|
||||
case 'h3':
|
||||
echo '<h3>'.$msg.'</h3>'.PHP_EOL;
|
||||
break;
|
||||
case 'list':
|
||||
echo '<ul>';
|
||||
foreach ($msg as $m) {
|
||||
echo '<li>'.$m.'</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
break;
|
||||
default:
|
||||
echo $msg.'<br/>'.PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
echo '<h2 class="success">Success!</h2>';
|
||||
}
|
||||
}
|
29
src/Tasks/CleanContentTask.php
Executable file
29
src/Tasks/CleanContentTask.php
Executable file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Tasks;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
|
||||
class CleanContentTask extends BuildTask
|
||||
{
|
||||
protected $title = 'Clean content task';
|
||||
|
||||
protected $description = 'Clean content task';
|
||||
|
||||
protected $enabled = true;
|
||||
|
||||
public function run($request)
|
||||
{
|
||||
$pages = SiteTree::get();
|
||||
foreach ($pages as $p) {
|
||||
$p->setField('Content', '');
|
||||
$p->write();
|
||||
echo '#'.$p->ID.' '.$p->getField('Title').'<br/>';
|
||||
}
|
||||
|
||||
die('Done!');
|
||||
}
|
||||
}
|
105
src/Tasks/QRCodeTask.php
Executable file
105
src/Tasks/QRCodeTask.php
Executable file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Tasks;
|
||||
|
||||
use Endroid\QrCode\ErrorCorrectionLevel;
|
||||
use Endroid\QrCode\LabelAlignment;
|
||||
use Endroid\QrCode\QrCode;
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Assets\Storage\AssetStore;
|
||||
use SilverStripe\Assets\Upload;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
|
||||
|
||||
class QRCodeTask extends BuildTask
|
||||
{
|
||||
protected $title = 'Generate website QR-code';
|
||||
|
||||
protected $description = 'Generate website QR-code';
|
||||
|
||||
protected $enabled = true;
|
||||
|
||||
public function run($request)
|
||||
{
|
||||
echo '<h1>'.$this->Title().'</h1>';
|
||||
|
||||
echo self::generateQR();
|
||||
|
||||
die('Done!');
|
||||
}
|
||||
|
||||
public static function generateQR()
|
||||
{
|
||||
$qrCode = new QrCode(Director::absoluteBaseURL());
|
||||
$qrCode->setSize(600);
|
||||
$qrCode->setMargin(10);
|
||||
|
||||
$qrCode->setWriterByName('png');
|
||||
$qrCode->setEncoding('UTF-8');
|
||||
$qrCode->setErrorCorrectionLevel(ErrorCorrectionLevel::HIGH());
|
||||
$qrCode->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]);
|
||||
$qrCode->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]);
|
||||
$qrCode->setLabel(Director::absoluteBaseURL(), 16, null, LabelAlignment::CENTER());
|
||||
/*$qrCode->setLogoPath('/'.File::join_paths(
|
||||
PUBLIC_PATH,
|
||||
RESOURCES_DIR,
|
||||
project(),
|
||||
'client', 'dist', 'icons',
|
||||
'apple-touch-icon-152x152.png'
|
||||
));
|
||||
$qrCode->setLogoSize(152, 152);*/
|
||||
$qrCode->setValidateResult(true);
|
||||
|
||||
|
||||
// Round block sizes to improve readability and make the blocks sharper in pixel based outputs (like png).
|
||||
// There are three approaches:
|
||||
$qrCode->setRoundBlockSize(true, QrCode::ROUND_BLOCK_SIZE_MODE_MARGIN); // The size of the qr code is shrinked, if necessary, but the size of the final image remains unchanged due to additional margin being added (default)
|
||||
$qrCode->setRoundBlockSize(true, QrCode::ROUND_BLOCK_SIZE_MODE_ENLARGE); // The size of the qr code and the final image is enlarged, if necessary
|
||||
$qrCode->setRoundBlockSize(true, QrCode::ROUND_BLOCK_SIZE_MODE_SHRINK); // The size of the qr code and the final image is shrinked, if necessary
|
||||
|
||||
// Set additional writer options (SvgWriter example)
|
||||
$qrCode->setWriterOptions(['exclude_xml_declaration' => true]);
|
||||
|
||||
// Directly output the QR code
|
||||
/*header('Content-Type: '.$qrCode->getContentType());
|
||||
echo $qrCode->writeString();
|
||||
die();*/
|
||||
|
||||
// Save it to a file
|
||||
$qrCode->writeFile(TEMP_PATH.'/qrcode.png');
|
||||
$res = self::getAssetStore()->setFromLocalFile(
|
||||
TEMP_PATH.'/qrcode.png',
|
||||
'qrcode.png', null, null,
|
||||
[
|
||||
'conflict' => AssetStore::CONFLICT_OVERWRITE,
|
||||
'visibility' => AssetStore::VISIBILITY_PUBLIC,
|
||||
]
|
||||
);
|
||||
|
||||
$img = Image::get()->filter([
|
||||
'ParentID' => 0,
|
||||
'FileFilename' => $res['Filename'],
|
||||
])->first();
|
||||
if(!$img) {
|
||||
$img = Image::create();
|
||||
}
|
||||
|
||||
$res['FileHash'] = $res['Hash'];
|
||||
$res['FileFilename'] = $res['Filename'];
|
||||
$res['ParentID'] = 0;
|
||||
|
||||
$img = $img->update($res);
|
||||
$img->write();
|
||||
$img->publishFile();
|
||||
|
||||
return '<img src="'.$qrCode->writeDataUri().'" width="300" alt="QR-code" /><br/>';
|
||||
}
|
||||
|
||||
protected static function getAssetStore()
|
||||
{
|
||||
return Injector::inst()->get(AssetStore::class);
|
||||
}
|
||||
}
|
47
src/Tasks/RestoreFilesTask.php
Executable file
47
src/Tasks/RestoreFilesTask.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Tasks;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
|
||||
class RestoreFilesTask extends BuildTask
|
||||
{
|
||||
protected $title = 'Restore Files Task';
|
||||
|
||||
protected $description = 'Restores file from specific folder';
|
||||
|
||||
protected $enabled = false;
|
||||
|
||||
public function run($request)
|
||||
{
|
||||
die('Specify path first');
|
||||
$path = '*<Path to the folder with files to be restored>*';
|
||||
|
||||
$files = array_diff(scandir($path), ['.','..']);
|
||||
foreach ($files as $fileName) {
|
||||
$file = File::get()->filter('Name', $fileName);
|
||||
if (!$file->count()) {
|
||||
echo '<b style="color:red">File name was not found at SS DB: '.$fileName.'</b><br/>'.PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($file as $f) {
|
||||
if ($f->exists()) {
|
||||
echo 'File #'.$f->ID.' already exists at SS file structure. <b style="color:green">'.$fileName.'</b><br/>' . PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
|
||||
echo 'Found non existing at SS file system file and found it at SS DB.'
|
||||
.' Creating the file #'.$f->ID.' at SS file system. "<b style="color:#053bff">' . $fileName . '"</b><br/>' . PHP_EOL;
|
||||
|
||||
$f->setFromLocalFile($path.'/'.$fileName);
|
||||
$f->write();
|
||||
$f->publishFile();
|
||||
}
|
||||
}
|
||||
|
||||
die('Success!');
|
||||
}
|
||||
}
|
229
src/Templates/DeferredRequirements.php
Executable file
229
src/Templates/DeferredRequirements.php
Executable file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpUnusedPrivateFieldInspection */
|
||||
|
||||
namespace A2nt\CMSNiceties\Templates;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\View\TemplateGlobalProvider;
|
||||
use SilverStripe\View\Requirements;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Path;
|
||||
use SilverStripe\FontAwesome\FontAwesomeField;
|
||||
|
||||
class DeferredRequirements implements TemplateGlobalProvider
|
||||
{
|
||||
private static $css = [];
|
||||
private static $js = [];
|
||||
private static $deferred = false;
|
||||
private static $static_domain;
|
||||
private static $version;
|
||||
private static $nojquery = false;
|
||||
private static $jquery_version = '3.4.1';
|
||||
private static $nofontawesome = false;
|
||||
private static $custom_requirements = [];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_template_global_variables(): array
|
||||
{
|
||||
return [
|
||||
'AutoRequirements' => 'Auto',
|
||||
'DeferedCSS' => 'loadCSS',
|
||||
'DeferedJS' => 'loadJS',
|
||||
'WebpackActive' => 'webpackActive',
|
||||
'EmptyImgSrc' => 'emptyImageSrc',
|
||||
];
|
||||
}
|
||||
|
||||
public static function Auto($class = false): string
|
||||
{
|
||||
$config = Config::inst()->get(self::class);
|
||||
$projectName = WebpackTemplateProvider::projectName();
|
||||
$mainTheme = WebpackTemplateProvider::mainTheme();
|
||||
$mainTheme = $mainTheme ?: $projectName;
|
||||
|
||||
$dir = Path::join(
|
||||
Director::publicFolder(),
|
||||
RESOURCES_DIR,
|
||||
$projectName,
|
||||
'client',
|
||||
'dist'
|
||||
);
|
||||
$cssPath = Path::join($dir, 'css');
|
||||
$jsPath = Path::join($dir, 'js');
|
||||
|
||||
// Initialization
|
||||
Requirements::block(THIRDPARTY_DIR.'/jquery/jquery.js');
|
||||
/*if (defined('FONT_AWESOME_DIR')) {
|
||||
Requirements::block(FONT_AWESOME_DIR.'/css/lib/font-awesome.min.css');
|
||||
}*/
|
||||
Requirements::set_force_js_to_bottom(true);
|
||||
|
||||
// Main libs
|
||||
if (!$config['nojquery']) {
|
||||
self::loadJS(
|
||||
'//ajax.googleapis.com/ajax/libs/jquery/'
|
||||
.$config['jquery_version'].'/jquery.min.js'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$config['noreact']) {
|
||||
if (!Director::isDev()) {
|
||||
self::loadJS('https://unpkg.com/react@17/umd/react.production.min.js');
|
||||
self::loadJS('https://unpkg.com/react-dom@17/umd/react-dom.production.min.js');
|
||||
} else {
|
||||
self::loadJS('https://unpkg.com/react@17/umd/react.development.js');
|
||||
self::loadJS('https://unpkg.com/react-dom@17/umd/react-dom.development.js');
|
||||
}
|
||||
}
|
||||
|
||||
// App libs
|
||||
if (!$config['nofontawesome']) {
|
||||
$v = !isset($config['fontawesome_version']) || !$config['fontawesome_version']
|
||||
? Config::inst()->get(FontAwesomeField::class, 'version')
|
||||
: $config['fontawesome_version'];
|
||||
|
||||
self::loadCSS('//use.fontawesome.com/releases/v'.$v.'/css/all.css');
|
||||
}
|
||||
|
||||
self::loadCSS($mainTheme.'.css');
|
||||
|
||||
// hot reloading
|
||||
/*if (self::webpackActive()) {
|
||||
self::loadJS('hot.js');
|
||||
}*/
|
||||
|
||||
self::loadJS($mainTheme.'.js');
|
||||
|
||||
// Custom controller requirements
|
||||
$curr_class = $class ?: get_class(Controller::curr());
|
||||
if (isset($config['custom_requirements'][$curr_class])) {
|
||||
foreach ($config['custom_requirements'][$curr_class] as $file) {
|
||||
if (strpos($file, '.css')) {
|
||||
self::loadCSS($file);
|
||||
}
|
||||
if (strpos($file, '.js')) {
|
||||
self::loadJS($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$curr_class = str_replace('\\', '.', $curr_class);
|
||||
|
||||
// Controller requirements
|
||||
$themePath = Path::join($cssPath, $mainTheme.'_'.$curr_class . '.css');
|
||||
$projectPath = Path::join($cssPath, $projectName.'_'.$curr_class . '.css');
|
||||
if ($mainTheme && file_exists($themePath)) {
|
||||
self::loadCSS($mainTheme.'_'.$curr_class . '.css');
|
||||
} elseif (file_exists($projectPath)) {
|
||||
self::loadCSS($projectName.'_'.$curr_class . '.css');
|
||||
}
|
||||
|
||||
$themePath = Path::join($jsPath, $mainTheme.'_'.$curr_class . '.js');
|
||||
$projectPath = Path::join($jsPath, $projectName.'_'.$curr_class . '.js');
|
||||
if ($mainTheme && file_exists($themePath)) {
|
||||
self::loadJS($mainTheme.'_'.$curr_class . '.js');
|
||||
} elseif (file_exists($projectPath)) {
|
||||
self::loadJS($projectName.'_'.$curr_class . '.js');
|
||||
}
|
||||
|
||||
return self::forTemplate();
|
||||
}
|
||||
|
||||
public static function loadCSS($css): void
|
||||
{
|
||||
$external = (mb_strpos($css, '//') === 0 || mb_strpos($css, 'http') === 0);
|
||||
if ($external) {
|
||||
self::$css[] = $css;
|
||||
} else {
|
||||
WebpackTemplateProvider::loadCSS($css);
|
||||
}
|
||||
}
|
||||
|
||||
public static function loadJS($js): void
|
||||
{
|
||||
/*$external = (mb_substr($js, 0, 2) === '//' || mb_substr($js, 0, 4) === 'http');
|
||||
if ($external || (self::getDeferred() && !self::_webpackActive())) {*/
|
||||
// webpack supposed to load external JS
|
||||
if (self::getDeferred() && !self::webpackActive()) {
|
||||
self::$js[] = $js;
|
||||
} else {
|
||||
WebpackTemplateProvider::loadJS($js);
|
||||
}
|
||||
}
|
||||
|
||||
public static function webpackActive(): bool
|
||||
{
|
||||
return WebpackTemplateProvider::isActive();
|
||||
}
|
||||
|
||||
public static function setDeferred($bool): void
|
||||
{
|
||||
Config::inst()->set(__CLASS__, 'deferred', $bool);
|
||||
}
|
||||
|
||||
public static function getDeferred(): bool
|
||||
{
|
||||
return self::config()['deferred'];
|
||||
}
|
||||
|
||||
public static function forTemplate(): string
|
||||
{
|
||||
$result = '';
|
||||
self::$css = array_unique(self::$css);
|
||||
foreach (self::$css as $css) {
|
||||
$result .= '<i class="defer-cs" data-load="' . self::get_url($css) . '"></i>';
|
||||
}
|
||||
|
||||
self::$js = array_unique(self::$js);
|
||||
foreach (self::$js as $js) {
|
||||
$result .= '<i class="defer-sc" data-load="' . self::get_url($js) . '"></i>';
|
||||
}
|
||||
|
||||
$result .=
|
||||
'<script>function lsc(a,b){var c=document.createElement("script");c.readyState'
|
||||
.'?c.onreadystatechange=function(){"loaded"!=c.readyState&&"complete"!=c.readyState||(c.onreadystatechange=null,b())}'
|
||||
.':c.onload=function(){b()},c.src=a,document.getElementsByTagName("body")[0].appendChild(c)}'
|
||||
.'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;if(s.item(i)!==null)lsc(s.item(i).getAttribute("data-load"),function(){lscd(i)});'
|
||||
.'</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function get_url($url): string
|
||||
{
|
||||
$config = self::config();
|
||||
|
||||
// external URL
|
||||
if (strpos($url, '//') !== false) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$path = WebpackTemplateProvider::toPublicPath($url);
|
||||
|
||||
$absolutePath = Director::getAbsFile($path);
|
||||
$hash = sha1_file($absolutePath);
|
||||
|
||||
$version = $config['version'] ? '&v='.$config['version'] : '';
|
||||
//$static_domain = $config['static_domain'];
|
||||
//$static_domain = $static_domain ?: '';
|
||||
|
||||
return Controller::join_links(WebpackTemplateProvider::toPublicPath($url), '?m='.$hash.$version);
|
||||
}
|
||||
|
||||
public static function emptyImageSrc(): string
|
||||
{
|
||||
return 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
||||
}
|
||||
|
||||
public static function config(): array
|
||||
{
|
||||
return Config::inst()->get(__CLASS__);
|
||||
}
|
||||
}
|
153
src/Templates/WebpackTemplateProvider.php
Executable file
153
src/Templates/WebpackTemplateProvider.php
Executable file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/** @noinspection PhpUnusedPrivateFieldInspection */
|
||||
|
||||
/**
|
||||
* Directs assets requests to Webpack server or to static files
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\Templates;
|
||||
|
||||
use SilverStripe\Core\Manifest\ModuleManifest;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\View\TemplateGlobalProvider;
|
||||
use SilverStripe\View\Requirements;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
|
||||
class WebpackTemplateProvider implements TemplateGlobalProvider
|
||||
{
|
||||
/**
|
||||
* @var int port number
|
||||
*/
|
||||
private static $port = 3000;
|
||||
|
||||
/**
|
||||
* @var string host name
|
||||
*/
|
||||
private static $hostname = 'localhost';
|
||||
|
||||
/**
|
||||
* @var string assets static files directory
|
||||
*/
|
||||
private static $dist = 'client/dist';
|
||||
private static $webp = false;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function get_template_global_variables(): array
|
||||
{
|
||||
return [
|
||||
'WebpackDevServer' => 'isActive',
|
||||
'WebpackCSS' => 'loadCSS',
|
||||
'WebpackJS' => 'loadJS',
|
||||
'ResourcesURL' => 'resourcesURL',
|
||||
'ProjectName' => 'themeName',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load CSS file
|
||||
* @param $path
|
||||
*/
|
||||
public static function loadCSS($path): void
|
||||
{
|
||||
/*if (self::isActive()) {
|
||||
return;
|
||||
}*/
|
||||
|
||||
Requirements::css(self::_getPath($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load JS file
|
||||
* @param $path
|
||||
*/
|
||||
public static function loadJS($path): void
|
||||
{
|
||||
Requirements::javascript(self::_getPath($path));
|
||||
}
|
||||
|
||||
public static function projectName(): string
|
||||
{
|
||||
return Config::inst()->get(ModuleManifest::class, 'project');
|
||||
}
|
||||
|
||||
public static function mainTheme()
|
||||
{
|
||||
$themes = Config::inst()->get(SSViewer::class, 'themes');
|
||||
return is_array($themes) && $themes[0] !== '$public' && $themes[0] !== '$default' ? $themes[0] : false;
|
||||
}
|
||||
|
||||
public static function resourcesURL($link = null): string
|
||||
{
|
||||
$cfg = self::config();
|
||||
|
||||
if ($cfg['webp'] && !self::isActive()) {
|
||||
$link = str_replace(['.png','.jpg','.jpeg'], '.webp', $link);
|
||||
}
|
||||
|
||||
return Controller::join_links(
|
||||
Director::baseURL(),
|
||||
'/resources/',
|
||||
self::projectName(),
|
||||
$cfg['dist'],
|
||||
'img',
|
||||
$link
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if dev mode is enabled and if webpack server is online
|
||||
* @return bool
|
||||
*/
|
||||
public static function isActive(): bool
|
||||
{
|
||||
$cfg = self::config();
|
||||
return Director::isDev() && @fsockopen(
|
||||
$cfg['HOSTNAME'],
|
||||
$cfg['PORT']
|
||||
);
|
||||
}
|
||||
|
||||
protected static function _getPath($path): string
|
||||
{
|
||||
return self::isActive() && strpos($path, '//') === false ?
|
||||
self::_toDevServerPath($path) :
|
||||
self::toPublicPath($path);
|
||||
}
|
||||
|
||||
protected static function _toDevServerPath($path): string
|
||||
{
|
||||
$cfg = self::config();
|
||||
return sprintf(
|
||||
'%s%s:%s/%s',
|
||||
($cfg['HTTPS'] ? 'https://' : 'http://'),
|
||||
$cfg['HOSTNAME'],
|
||||
$cfg['PORT'],
|
||||
basename($path)
|
||||
//Controller::join_links($cfg['APPDIR'], $cfg['SRC'], basename($path))
|
||||
);
|
||||
}
|
||||
|
||||
public static function toPublicPath($path): string
|
||||
{
|
||||
$cfg = self::config();
|
||||
return strpos($path, '//') === false ?
|
||||
Controller::join_links(
|
||||
RESOURCES_DIR,
|
||||
self::projectName(),
|
||||
$cfg['dist'],
|
||||
(strpos($path, '.css') ? 'css' : 'js'),
|
||||
$path
|
||||
)
|
||||
: $path;
|
||||
}
|
||||
|
||||
public static function config(): array
|
||||
{
|
||||
return Config::inst()->get(__CLASS__);
|
||||
}
|
||||
}
|
142
src/Tests/TestServer.php
Executable file
142
src/Tests/TestServer.php
Executable file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: tony
|
||||
* Date: 10/31/18
|
||||
* Time: 5:31 AM
|
||||
*/
|
||||
|
||||
namespace A2nt\CMSNiceties\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\Dev\Deprecation;
|
||||
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 Server</h2>';
|
||||
echo self::success('BASE_PATH: <b>'.BASE_PATH.'</b>');
|
||||
echo self::success('PHP: <b>'.phpversion().'</b>');
|
||||
|
||||
$v = Deprecation::dump_settings()['version'];
|
||||
if ($v) {
|
||||
echo self::success('SilverStipe version: <b>'.$v.'</b>');
|
||||
} else {
|
||||
echo self::success('SilverStipe version unknown: <b>'
|
||||
.(file_exists(BASE_PATH.'/framework') ? '3.x.x' : '4.x.x')
|
||||
.'</b>');
|
||||
}
|
||||
|
||||
echo self::success('Memory limit: <b>'.ini_get('memory_limit').'</b>');
|
||||
|
||||
|
||||
if (is_writable(TEMP_FOLDER)) {
|
||||
echo self::success('TMP (cache) dir <b>'.TEMP_FOLDER.'</b> dir is writable.');
|
||||
} else {
|
||||
echo self::error('TMP (cache) dir <b>'.TEMP_FOLDER.'</b> dir is no writable!');
|
||||
}
|
||||
|
||||
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(
|
||||
self::memstring2bytes($maxUpload),
|
||||
self::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>';
|
||||
}
|
||||
|
||||
if (is_writable(ASSETS_DIR)) {
|
||||
echo self::success('Assets dir <b>'.ASSETS_DIR.'</b> dir is writable.');
|
||||
} else {
|
||||
echo self::error('Assets dir <b>'.ASSETS_DIR.'</b> dir is no writable!');
|
||||
}
|
||||
|
||||
if (function_exists('imagewebp')) {
|
||||
echo self::success('WebP is available');
|
||||
} else {
|
||||
echo self::error('WebP is not available');
|
||||
}
|
||||
|
||||
|
||||
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>';
|
||||
}
|
||||
|
||||
public static function memstring2bytes($memString)
|
||||
{
|
||||
// Remove non-unit characters from the size
|
||||
$unit = preg_replace('/[^bkmgtpezy]/i', '', $memString);
|
||||
// Remove non-numeric characters from the size
|
||||
$size = preg_replace('/[^0-9\.]/', '', $memString);
|
||||
|
||||
if ($unit) {
|
||||
// Find the position of the unit in the ordered string which is the power
|
||||
// of magnitude to multiply a kilobyte by
|
||||
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
|
||||
}
|
||||
|
||||
return round($size);
|
||||
}
|
||||
}
|
18
src/Traits/PaginatedListing.php
Executable file
18
src/Traits/PaginatedListing.php
Executable file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Traits;
|
||||
|
||||
trait PaginatedListing
|
||||
{
|
||||
private $filter = [];
|
||||
|
||||
public function NextPage($pageID = null)
|
||||
{
|
||||
$vars = $this->getRequest()->requestVars();
|
||||
$vars = array_filter($vars);
|
||||
$vars['page'] = $pageID ? $pageID : '2';
|
||||
|
||||
return $this->Link('?'.http_build_query($vars));
|
||||
}
|
||||
}
|
66
src/Widgets/BannerWidget.php
Executable file
66
src/Widgets/BannerWidget.php
Executable file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
if (!class_exists(Widget::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
class BannerWidget extends Widget
|
||||
{
|
||||
private static $title = 'Banner';
|
||||
private static $cmsTitle = 'Banner';
|
||||
private static $description = 'Shows banner with image and link.';
|
||||
private static $icon = '<i class="icon font-icon-block-banner"></i>';
|
||||
private static $table_name = 'BannerWidget';
|
||||
|
||||
private static $has_one = [
|
||||
'Image' => Image::class,
|
||||
'Link' => Link::class,
|
||||
];
|
||||
|
||||
private static $owns = [
|
||||
'Image',
|
||||
'Link',
|
||||
];
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->push(UploadField::create('Image', 'Image (minimal width 301px)')
|
||||
->setAllowedFileCategories(['image/supported']));
|
||||
|
||||
$fields->push(LinkField::create('LinkID', 'Link'));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private $_random;
|
||||
public function Random()
|
||||
{
|
||||
if (!$this->_random) {
|
||||
$this->_random = self::get()->filter('Enabled', true)->sort('RAND()')->first();
|
||||
}
|
||||
|
||||
return $this->_random;
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
$title = $this->getField('Title');
|
||||
$img = $this->Image();
|
||||
if(!$title && $img) {
|
||||
$this->setField('Title', $img->getTitle());
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
}
|
37
src/Widgets/ContentWidget.php
Executable file
37
src/Widgets/ContentWidget.php
Executable file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
if (!class_exists(Widget::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
class ContentWidget extends Widget
|
||||
{
|
||||
private static $title = 'Content';
|
||||
private static $cmsTitle = 'Content';
|
||||
private static $description = 'Shows text content.';
|
||||
private static $icon = '<i class="icon font-icon-block-content"></i>';
|
||||
private static $table_name = 'ContentWidget';
|
||||
|
||||
private static $db = [
|
||||
'Text' => 'HTMLText',
|
||||
];
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->push(HTMLEditorField::create('Text'));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
60
src/Widgets/ElementWidget.php
Executable file
60
src/Widgets/ElementWidget.php
Executable file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use DNADesign\Elemental\Models\BaseElement;
|
||||
use DNADesign\ElementalList\Model\ElementList;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
if (!class_exists(Widget::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
class ElementWidget extends Widget
|
||||
{
|
||||
private static $title = 'Virtual Element';
|
||||
private static $cmsTitle = 'Virtual Element';
|
||||
private static $description = 'Adds existing element to side bar';
|
||||
private static $icon = '<i class="icon font-icon-block-banner"></i>';
|
||||
private static $table_name = 'ElementWidget';
|
||||
|
||||
private static $has_one = [
|
||||
'Element' => BaseElement::class,
|
||||
];
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->push(
|
||||
DropdownField::create(
|
||||
'ElementID',
|
||||
'Displayed Element',
|
||||
BaseElement::get()
|
||||
->filter(['AvailableGlobally' => true])
|
||||
->exclude(['ClassName' => ElementList::class])
|
||||
)
|
||||
/*TreeDropdownField::create(
|
||||
'ElementID',
|
||||
'Displayed Element',
|
||||
SiteTree::class
|
||||
)->setFilterFunction(static function($el){
|
||||
return (bool) $el->getField('ElementalArea')->Elements()->count();
|
||||
})*/
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function SimpleClassName()
|
||||
{
|
||||
$el = $this->getField('Element');
|
||||
var_dump($el);
|
||||
die();
|
||||
return $el->getSimpleClassName();
|
||||
}
|
||||
}
|
56
src/Widgets/LinksWidget.php
Executable file
56
src/Widgets/LinksWidget.php
Executable file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use Sheadawson\Linkable\Forms\LinkField;
|
||||
use Sheadawson\Linkable\Models\Link;
|
||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
if (!class_exists(Widget::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
class LinksWidget extends Widget
|
||||
{
|
||||
private static $title = 'Links';
|
||||
private static $cmsTitle = 'Links';
|
||||
private static $description = 'Shows listing of links.';
|
||||
private static $icon = '<i class="icon font-icon-list"></i>';
|
||||
private static $table_name = 'LinksWidget';
|
||||
|
||||
private static $many_many = [
|
||||
'Links' => Link::class,
|
||||
];
|
||||
|
||||
private static $owns = [
|
||||
'Links',
|
||||
];
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
if($this->ID) {
|
||||
$fields->push(GridField::create(
|
||||
'Links',
|
||||
'',
|
||||
$this->Links(),
|
||||
GridFieldConfig_RecordEditor::create()
|
||||
));
|
||||
}else{
|
||||
$fields->push(LiteralField::create(
|
||||
'Note',
|
||||
'<p class="alert alert-warning"><b>Note:</b> The widget needs to be saved before adding a link.'
|
||||
.' Enter the Title and click "+ Create" button at the bottom left corner of the screen</p>')
|
||||
);
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
53
src/Widgets/SubmenuWidget.php
Executable file
53
src/Widgets/SubmenuWidget.php
Executable file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
if (!class_exists(Widget::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
class SubmenuWidget extends Widget
|
||||
{
|
||||
private static $title = 'Sub-Menu';
|
||||
private static $cmsTitle = 'Sub-Menu';
|
||||
private static $description = 'Shows sub menu.';
|
||||
private static $icon = '<i class="icon font-icon-tree"></i>';
|
||||
private static $table_name = 'SubmenuWidget';
|
||||
|
||||
private static $db = [
|
||||
'TopLevelSubmenu' => 'Boolean(1)',
|
||||
];
|
||||
|
||||
public function getPage()
|
||||
{
|
||||
$area = $this->Parent();
|
||||
return \Page::get()->filter('SideBarID', $area->ID)->first();
|
||||
}
|
||||
|
||||
public function getSubmenu()
|
||||
{
|
||||
$page = $this->getPage();
|
||||
|
||||
if(!$this->getField('TopLevelSubmenu')) {
|
||||
return $page->Children();
|
||||
}
|
||||
|
||||
return $page->Level(1)->Children();
|
||||
}
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->push(CheckboxField::create(
|
||||
'TopLevelSubmenu',
|
||||
'Display sub-menu starting from the top level (otherwise current page children will be displayed)'
|
||||
));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
157
src/Widgets/WidgetAreaField.php
Executable file
157
src/Widgets/WidgetAreaField.php
Executable file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use DNADesign\Elemental\Controllers\ElementalAreaController;
|
||||
use DNADesign\Elemental\Forms\ElementalAreaConfig;
|
||||
use DNADesign\Elemental\Forms\ElementalAreaField;
|
||||
use DNADesign\Elemental\Models\BaseElement;
|
||||
use DNADesign\Elemental\Models\ElementalArea;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\FieldGroup;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
|
||||
use SilverStripe\Forms\GridField\GridFieldAddNewButton;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_Base;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||
use SilverStripe\Forms\TabSet;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
|
||||
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
|
||||
|
||||
class WidgetAreaField extends GridField
|
||||
{
|
||||
/**
|
||||
* @var ElementalArea $area
|
||||
*/
|
||||
protected $area;
|
||||
|
||||
/**
|
||||
* @var array $type
|
||||
*/
|
||||
protected $types = [];
|
||||
|
||||
/**
|
||||
* @var null
|
||||
*/
|
||||
protected $inputType = null;
|
||||
|
||||
protected $modelClassName = Widget::class;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param ElementalArea $area
|
||||
* @param string[] $blockTypes
|
||||
*/
|
||||
public function __construct($name, WidgetArea $area, array $blockTypes)
|
||||
{
|
||||
$this->setTypes($blockTypes);
|
||||
|
||||
$config = GridFieldConfig_Base::create();
|
||||
|
||||
$config->getComponentByType(GridFieldDataColumns::class)->setDisplayFields([
|
||||
'Icon' => '',
|
||||
'Title' => 'Title',
|
||||
'Enabled' => 'Enabled',
|
||||
])->setFieldFormatting([
|
||||
'Icon' => static function ($v, Widget $item) {
|
||||
return '<span style="font-size:2rem">'.$item::config()->get('icon').'</span>';
|
||||
},
|
||||
'Enabled' => static function ($v, Widget $item) {
|
||||
return $item->getField('Enabled') ? 'Yes' : 'No';
|
||||
},
|
||||
]);
|
||||
|
||||
$config->addComponent(new GridFieldEditButton());
|
||||
$config->addComponent(new GridFieldDeleteAction(false));
|
||||
$config->addComponent(new GridFieldDetailForm(null, false, false));
|
||||
$config->addComponent(new GridFieldSortableRows('Sort'));
|
||||
|
||||
if (!empty($blockTypes)) {
|
||||
/** @var GridFieldAddNewMultiClass $adder */
|
||||
$adder = Injector::inst()->create(GridFieldAddNewMultiClass::class);
|
||||
$adder->setClasses($blockTypes);
|
||||
$config->addComponent($adder);
|
||||
}
|
||||
|
||||
// By default, no need for a title on the editor. If there is more than one area then use `setTitle` to describe
|
||||
parent::__construct($name, '', $area->Widgets(), $config);
|
||||
|
||||
$this->area = $area;
|
||||
$this->addExtraClass('element-editor__container no-change-track');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTypes($types)
|
||||
{
|
||||
$this->types = $types;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
$types = $this->types;
|
||||
|
||||
$this->extend('updateGetTypes', $types);
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ElementalArea
|
||||
*/
|
||||
public function getArea()
|
||||
{
|
||||
return $this->area;
|
||||
}
|
||||
|
||||
public function saveInto(DataObjectInterface $dataObject)
|
||||
{
|
||||
parent::saveInto($dataObject);
|
||||
|
||||
$elementData = $this->Value();
|
||||
$idPrefixLength = strlen(sprintf(ElementalAreaController::FORM_NAME_TEMPLATE, ''));
|
||||
|
||||
if (!$elementData) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($elementData as $form => $data) {
|
||||
// Extract the ID
|
||||
$elementId = (int) substr($form, $idPrefixLength);
|
||||
|
||||
// @var BaseElement $element
|
||||
$element = $this->getArea()->Widgets()->byID($elementId);
|
||||
|
||||
if (!$element) {
|
||||
// Ignore invalid elements
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = ElementalAreaController::removeNamespacesFromFields($data, $element->ID);
|
||||
|
||||
$element->updateFromFormData($data);
|
||||
$element->write();
|
||||
}
|
||||
}
|
||||
}
|
50
src/Widgets/WidgetExtension.php
Executable file
50
src/Widgets/WidgetExtension.php
Executable file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
|
||||
use DNADesign\Elemental\Forms\TextCheckboxGroupField;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
class WidgetExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'ShowTitle' => 'Boolean(1)',
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
// Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group
|
||||
$fields->removeByName('ShowTitle');
|
||||
$fields->replaceField(
|
||||
'Title',
|
||||
TextCheckboxGroupField::create()
|
||||
->setName('Title')
|
||||
);
|
||||
$fields->push(TreeDropdownField::create(
|
||||
'MovePageID', 'Move widget to page', SiteTree::class
|
||||
));
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
$obj = $this->owner;
|
||||
$moveID = $obj->MovePageID;
|
||||
if ($moveID) {
|
||||
$page = \Page::get()->byID($moveID);
|
||||
if($page) {
|
||||
$sidebarID = $page->getField('SideBarID');
|
||||
if($sidebarID) {
|
||||
$obj->setField('ParentID', $sidebarID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
}
|
49
src/Widgets/WidgetPageExtension.php
Executable file
49
src/Widgets/WidgetPageExtension.php
Executable file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace A2nt\CMSNiceties\Widgets;
|
||||
|
||||
use DNADesign\Elemental\Forms\ElementalAreaField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
class WidgetPageExtension extends \SilverStripe\Widgets\Extensions\WidgetPageExtension
|
||||
{
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
parent::updateCMSFields($fields);
|
||||
|
||||
$tab = $fields->findOrMakeTab('Root.Widgets');
|
||||
|
||||
$tab->setTitle('Sidebar');
|
||||
|
||||
$tab->removeByName('SideBar');
|
||||
|
||||
$widgetTypes = WidgetAreaEditor::create('Sidebar')->AvailableWidgets();
|
||||
$available = [];
|
||||
/** @var Widget $type */
|
||||
foreach ($widgetTypes as $type) {
|
||||
$available[get_class($type)] = $type->getCMSTitle();
|
||||
}
|
||||
|
||||
$tab->push(WidgetAreaField::create(
|
||||
'SideBar',
|
||||
$this->owner->Sidebar(),
|
||||
$available
|
||||
));
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
|
||||
if (!$this->owner->getField('SideBarID')) {
|
||||
$area = WidgetArea::create();
|
||||
$area->write();
|
||||
|
||||
$this->owner->setField('SideBarID', $area->ID);
|
||||
}
|
||||
}
|
||||
}
|
8
templates/A2nt/CMSNiceties/Widgets/BannerWidget.ss
Executable file
8
templates/A2nt/CMSNiceties/Widgets/BannerWidget.ss
Executable file
@ -0,0 +1,8 @@
|
||||
<img src="$Image.FocusFill(432,315).URL" alt="$Title" />
|
||||
<% if $Link %>
|
||||
<% with $Link %>
|
||||
<a href="$URL"<% if $OpenInNewWindow %> target="_blank"<% end_if %> class="stretched-link">
|
||||
<span class="visually-hidden">$Up.Title</span>
|
||||
</a>
|
||||
<% end_with %>
|
||||
<% end_if %>
|
3
templates/A2nt/CMSNiceties/Widgets/ContentWidget.ss
Executable file
3
templates/A2nt/CMSNiceties/Widgets/ContentWidget.ss
Executable file
@ -0,0 +1,3 @@
|
||||
<% if $Text %>
|
||||
<div class="typography">$Text</div>
|
||||
<% end_if %>
|
9
templates/A2nt/CMSNiceties/Widgets/ElementWidget.ss
Executable file
9
templates/A2nt/CMSNiceties/Widgets/ElementWidget.ss
Executable file
@ -0,0 +1,9 @@
|
||||
<% if $Element %>
|
||||
<% with $Element %>
|
||||
<div class="element element__{$SimpleClassName}<% if $StyleVariant %> $StyleVariant<% end_if %><% if $ExtraClass %> $ExtraClass<% end_if %>" id="$Anchor">
|
||||
<div class="element-container<% if $ContainerClass %> $ContainerClass<% end_if %>">
|
||||
$forTemplate
|
||||
</div>
|
||||
</div>
|
||||
<% end_with %>
|
||||
<% end_if %>
|
18
templates/A2nt/CMSNiceties/Widgets/SubmenuWidget.ss
Executable file
18
templates/A2nt/CMSNiceties/Widgets/SubmenuWidget.ss
Executable file
@ -0,0 +1,18 @@
|
||||
<% if $Submenu %>
|
||||
<nav>
|
||||
<ul class="nav flex-column">
|
||||
<% with $Page.Level(1) %>
|
||||
<li class="nav-item-level1 nav-item {$CSSClass} $ExtraClass">
|
||||
<b class="nav-link">
|
||||
$MenuTitle.XML
|
||||
<% if $isCurrent || $isSection %><i class="visually-hidden">(current)</i><% end_if %>
|
||||
</b>
|
||||
</li>
|
||||
<% end_with %>
|
||||
|
||||
<% loop $Submenu %>
|
||||
<% include NavItem %>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% end_if %>
|
21
templates/BreadcrumbsTemplate.ss
Executable file
21
templates/BreadcrumbsTemplate.ss
Executable file
@ -0,0 +1,21 @@
|
||||
<% if $Pages %>
|
||||
<div class="$DefaultContainer">
|
||||
<nav class="breadcrumbs" aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="/" class="breadcrumb-link">Home</a>
|
||||
</li>
|
||||
<% loop $Pages %>
|
||||
<li
|
||||
class="breadcrumb-item<% if $Last %> current active<% end_if %>"
|
||||
<% if $Last %> aria-current="page"<% end_if %>
|
||||
>
|
||||
<% if not Up.Unlinked %><a href="$Link" class="graphql-page breadcrumb-link breadcrumb-link__$Pos"><% end_if %>
|
||||
$MenuTitle.XML
|
||||
<% if not Up.Unlinked %></a><% end_if %>
|
||||
</li>
|
||||
<% end_loop %>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<% end_if %>
|
1
templates/GraphQLPage.ss
Executable file
1
templates/GraphQLPage.ss
Executable file
@ -0,0 +1 @@
|
||||
<% include MainContent Layout=$Layout %>
|
29
templates/Page.ss
Executable file
29
templates/Page.ss
Executable file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="$ContentLocale.ATT" dir="$i18nScriptDirection.ATT">
|
||||
<%-- manifest="/cache.appcache" --%>
|
||||
<head>
|
||||
<% include MetaHead %>
|
||||
</head>
|
||||
|
||||
<body<% with $SiteConfig %> data-default-lng="$Longitude" data-default-lat="$Latitude"<% end_with %>>
|
||||
<div class="wrapper">
|
||||
<% include First %>
|
||||
|
||||
<div id="MetaLightboxApp"></div>
|
||||
|
||||
<header id="Header">
|
||||
<% include Header %>
|
||||
</header>
|
||||
|
||||
<main id="MainContent" class="page-content" data-ajax-region="LayoutAjax">
|
||||
<% include MainContent Layout=$Layout %>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer id="Footer" class="site-footer footer">
|
||||
<% include Footer %>
|
||||
</footer>
|
||||
|
||||
<% include Last %>
|
||||
</body>
|
||||
</html>
|
4
templates/WidgetHolder.ss
Executable file
4
templates/WidgetHolder.ss
Executable file
@ -0,0 +1,4 @@
|
||||
<nav class="secondary element element__widget widget__$ClassName {$SimpleClassName}">
|
||||
<% if $ShowTitle && $Title %><h2 class="widget-title">$Title</h2><% end_if %>
|
||||
$Content
|
||||
</nav>
|
Loading…
Reference in New Issue
Block a user