This commit is contained in:
Tony Air 2021-06-08 15:38:35 +07:00
parent ff1d0fae9e
commit 8f2c2693ac
23 changed files with 1581 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

1
_config.php Executable file
View File

@ -0,0 +1 @@
<?php

3
_config/config.yml Normal file
View File

@ -0,0 +1,3 @@
---
Name: elemental-basics
---

34
composer.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "a2nt/silverstripe-elemental-basics",
"description": "Some useful elemental blocks",
"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",
"dnadesign/silverstripe-elemental": "*",
"dnadesign/silverstripe-elemental-list": "*",
"betterbrief/silverstripe-googlemapfield": "*",
"symbiote/silverstripe-gridfieldextensions": "*",
"dynamic/silverstripe-elemental-flexslider": "*",
"dynamic/silverstripe-elemental-image": "*",
"sheadawson/silverstripe-linkable": "*",
"a2nt/silverstripe-mapboxfield": "dev-master",
},
"require-dev": {
"phpunit/phpunit": "^5.7"
},
"autoload": {
"psr-4": {
"A2nt\\ElementalBasics\\": "src/"
}
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace A2nt\ElementalBasics\Controllers;
use DNADesign\Elemental\Controllers\ElementController;
use App\Templates\DeferredRequirements;
class MapElementController extends ElementController
{
public function init()
{
parent::init();
DeferredRequirements::loadCSS('https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.css');
DeferredRequirements::loadJS('https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.js');
DeferredRequirements::loadCSS('app_Site.Controllers.MapElementController.css');
DeferredRequirements::loadJS('app_Site.Controllers.MapElementController.js');
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use DNADesign\ElementalList\Model\ElementList;
class AccordionElement extends ElementList
{
private static $singular_name = 'Accordion Element';
private static $plural_name = 'Accordion Element';
private static $description = 'Displays Accordion of Elements';
private static $table_name = 'AccordionElement';
public function getType()
{
return self::$singular_name;
}
public function Accordion()
{
return $this->Elements()->renderWith(static::class.'_AccordionArea');
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use DNADesign\Elemental\Models\ElementContent;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Assets\Image;
use SilverStripe\Forms\TextareaField;
class CustomSnippetElement extends ElementContent
{
private static $singular_name = 'Custom Snippet';
private static $plural_name = 'Custom Snippets';
private static $description = 'Displays Custom Snippet';
private static $table_name = 'CustomSnippetElement';
public function getType()
{
return self::$singular_name;
}
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->replaceField(
'HTML',
TextareaField::create('HTML')
);
return $fields;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace A2nt\ElementalBasics\Elements;
use SilverStripe\ORM\DataExtension;
class ElementListExtension extends DataExtension
{
public function getControllerName()
{
return EmptyPageController::class;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace A2nt\ElementalBasics\Elements;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\CMS\Model\SiteTree;
class ElementalContentControllerExtension extends \DNADesign\Elemental\Extensions\ElementalContentControllerExtension
{
private static $allowed_actions = array(
'handleElement'
);
public function handleElement()
{
$id = $this->owner->getRequest()->param('ID');
if (!$id) {
user_error('No element ID supplied', E_USER_ERROR);
return false;
}
/** @var SiteTree $elementOwner */
//$elementOwner = $this->owner->data();
/*$elementalAreaRelations = $this->owner->getElementalRelations();
if (!$elementalAreaRelations) {
user_error(get_class($this->owner) . ' has no ElementalArea relationships', E_USER_ERROR);
return false;
}*/
$element = BaseElement::get()->byID($id);
if ($element) {
return $element->getController();
}
user_error('Element $id not found', E_USER_ERROR);
return false;
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace A2nt\ElementalBasics\Elements;
use SilverStripe\SiteConfig\SiteConfig;
class EmptyPageController extends \PageController
{
public function __construct($dataRecord = null)
{
parent::__construct();
$this->dataRecord->Title = SiteConfig::current_site_config()->getField('Title');
$this->setFailover($this->dataRecord);
}
public static function DefaultContainer()
{
return \Page::DefaultContainer();
}
}

103
src/Elements/InstagramElement.php Executable file
View File

@ -0,0 +1,103 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\Core\Convert;
class InstagramElement extends BaseElement
{
private static $singular_name = 'Instagram';
private static $plural_name = 'Instagram Elements';
private static $description = 'Displays Instagram posts';
private static $table_name = 'InstagramElement';
private static $db = [
'Username' => 'Varchar(255)',
'Tag' => 'Varchar(255)',
'DisplayProfile' => 'Boolean(0)',
'DisplayBiography' => 'Boolean(0)',
'DisplayGallery' => 'Boolean(0)',
'DisplayCaptions' => 'Boolean(0)',
];
private static $defaults = [
'DisplayGallery' => true,
];
public function getType()
{
return self::$singular_name;
}
/**
* @return array
*/
public function getAttributes(): array
{
return [
'data-username' => $this->Username,
'data-display-profile' => $this->DisplayProfile,
'data-display-biography' => $this->DisplayBiography,
'data-display-gallery' => $this->DisplayGallery,
'data-display-captions' => $this->DisplayCaptions,
'data-items' => 12,
];
}
/**
* Custom attributes to process.
*
* @param array $attributes
*
* @return string
*/
public function AttributesHTML($attributes = null): string
{
if (!$attributes) {
$attributes = $this->getAttributes();
}
$attributes = (array) $attributes;
$attributes = array_filter($attributes, static function ($v) {
return ($v || $v === 0 || $v === '0');
});
// Create markup
$parts = [];
foreach ($attributes as $name => $value) {
if ($value === true) {
$value = $name;
} else if (is_scalar($value)) {
$value = (string) $value;
} else {
$value = json_encode($value);
}
$parts[] = sprintf('%s=%s', Convert::raw2att($name), Convert::raw2att($value));
}
return implode(' ', $parts);
}
public function FeedLink()
{
return 'https://www.instagram.com/'.($this->Username ? $this->Username : 'explore/tags/'.$this->Tag).'/';
}
public function FeedTitle()
{
return ($this->Username ? '@'.$this->Username : '#'.$this->Tag);
}
}

98
src/Elements/MapElement.php Executable file
View File

@ -0,0 +1,98 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use A2nt\SilverStripeMapboxField\MapboxField;
use BetterBrief\GoogleMapField;
use Colymba\BulkManager\BulkManager;
use DNADesign\Elemental\Models\ElementContent;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\Forms\NumericField;
use A2nt\ElementalBasics\Controllers\MapElementController;
use A2nt\ElementalBasics\Extensions\MapExtension;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
class MapElement extends ElementContent
{
private static $icon = 'font-icon-globe-1';
private static $singular_name = 'Map Element';
private static $plural_name = 'Map Element';
private static $description = 'Displays dynamic map';
private static $table_name = 'MapElement';
private static $controller_class = MapElementController::class;
private static $map_type = 'mapbox';
private static $extensions = [
MapExtension::class,
];
public function getType():string
{
return self::$singular_name;
}
public function getCMSFields(): FieldList
{
$fields = parent::getCMSFields();
$fields->remove('HTML');
$tab = $fields->findOrMakeTab('Root.MapPins');
$tab->setTitle('Description');
$tab->push(HTMLEditorField::create('HTML', 'Content'));
$fields->addFieldsToTab('Root.Main', [
NumericField::create('MapZoom', 'Initial Map Zoom (enter a number from 0 to 24)'),
GridField::create(
'Locations',
'Locations',
$this->owner->Locations(),
$cfg = GridFieldConfig_RelationEditor::create(100)
)
]);
$cfg->getComponentByType(GridFieldDataColumns::class)->setFieldFormatting([
'ShowAtMap' => static function ($v, $obj) {
return $v ? 'YES' : 'NO';
}
]);
$cfg->addComponent(new BulkManager());
return $fields;
}
public static function MapAPIKey(): string
{
$type = self::config()->get('map_type');
switch ($type) {
case 'mapbox':
$key = MapboxField::getAccessToken();
break;
case 'google-maps':
$cfg = Config::inst()->get(GoogleMapField::class, 'default_options');
$key = $cfg['api_key'];
break;
default:
$key = '';
break;
}
return $key;
}
}

133
src/Elements/SliderElement.php Executable file
View File

@ -0,0 +1,133 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use Dynamic\Elements\Flexslider\Elements\ElementSlideshow;
use Dynamic\FlexSlider\Model\SlideImage;
use Dynamic\FlexSlider\ORM\FlexSlider;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\NumericField;
use SilverStripe\Forms\ReadonlyField;
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
class SliderElement extends ElementSlideshow
{
private static $singular_name = 'Slider';
private static $plural_name = 'Sliders';
private static $description = 'Displays slide show';
private static $table_name = 'SliderElement';
private static $slide_width = 2140;
private static $slide_height = 700;
private static $db = [
'Interval' => 'Int',
];
private static $extensions = [
FlexSlider::class,
];
private static $owns = [
'Slides',
];
private $items;
public function getType()
{
return self::$singular_name;
}
public function getSlideWidth()
{
return self::config()->get('slide_width');
}
public function getSlideHeight()
{
return self::config()->get('slide_height');
}
public function getCMSFields()
{
$fields = parent::getCMSFields();
// remove in case you don't need to provide this functionality
$fields->removeByName([
'ConfigHD',
'Animation',
'Loop',
'Animate',
'ThumbnailNav',
'SliderControlNav',
'SliderDirectionNav',
'CarouselControlNav',
'CarouselDirectionNav',
'CarouselThumbnailCt',
]);
$fields->addFieldsToTab('Root.Settings', [
NumericField::create('Interval', 'Auto-play Interval'),
]);
$grid = $fields->dataFieldByName('Slides');
if ($grid) {
$config = $grid->getConfig();
$columns = new GridFieldEditableColumns();
$columns->setDisplayFields([
'Hide' => [
'title' => 'Hide it?',
'field' => CheckboxField::class,
],
]);
$config->addComponent($columns);
}
return $fields;
}
/**
* @return mixed
*/
public function getSlideShow()
{
if ($this->items) {
return $this->items;
}
$date = date('Y-m-d H:i:s');
$this->items = $this->Slides()->filter([
'Hide' => '0',
])->filterByCallback(static function ($item, $list) use ($date) {
$on = $item->getField('DateOn');
$off = $item->getField('DateOff');
return ($on <= $date) && (!$off || $off > $date);
})->sort('SortOrder');
return $this->items;
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
if (!$this->getField('Interval')) {
$this->setField('Interval', 5000);
}
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:54 PM
*/
namespace A2nt\ElementalBasics\Elements;
use DNADesign\Elemental\Models\BaseElement;
use A2nt\ElementalBasics\Models\TeamMember;
class TeamMembersElement extends BaseElement
{
private static $singular_name = 'Team Members';
private static $plural_name = 'Team Members';
private static $description = 'Displays random Team Members';
public function getType()
{
return self::$singular_name;
}
public function Members()
{
return TeamMember::get()->sort('RAND()');
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/23/18
* Time: 1:23 PM
*/
namespace A2nt\ElementalBasics\Extensions;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\LiteralField;
class BaseElementEx extends DataExtension
{
public function updateCMSFields(FieldList $fields)
{
$obj = $this->owner;
parent::updateCMSFields($fields);
$tab = $fields->findOrMakeTab('Root.Settings');
$tab->push(LiteralField::create(
'ClassName',
'<div class="form-group field text">'
.'<div class="form__field-label">Class</div>'
.'<div class="form__field-holder">'.$obj->getField('ClassName').'</div>'
.'</div>'
));
if($this->owner->ID) {
$tab->push(TreeDropdownField::create(
'MoveElementIDToPage',
'Move an element to page',
SiteTree::class
)->setEmptyString('(select an element to move)'));
}
}
public static function MoveElementToPage($ID, $moveToID)
{
$el = BaseElement::get()->byID($ID);
$page = SiteTree::get()->byID($moveToID);
if (!$page || !$el) {
return false;
}
$el->setField('ParentID', $page->ElementalArea()->ID);
$el->write();
return true;
}
public function updateCMSEditLink(&$link): void
{
if (!$this->owner->inlineEditable()) {
$page = $this->owner->getPage();
if (!$page || $page instanceof SiteTree) {
return;
}
// As non-page DataObject's are managed via GridFields, we have to grab their CMS edit URL
// and replace the trailing /edit/ with a link to the nested ElementalArea edit form
$relationName = $this->owner->getAreaRelationName();
$link = preg_replace(
'/edit\/?$/',
"ItemEditForm/field/{$relationName}/item/{$this->owner->ID}/edit/",
$page->CMSEditLink()
);
}
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
$moveID = $this->owner->getField('MoveElementIDToPage');
if($this->owner->ID && $moveID) {
self::MoveElementToPage($this->owner->ID, $moveID);
}
}
}

View File

@ -0,0 +1,15 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/24/18
* Time: 2:35 PM
*/
namespace A2nt\ElementalBasics\Extensions;
use SilverStripe\ORM\DataExtension;
class ElementContentWidget extends DataExtension
{
}

View File

@ -0,0 +1,186 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/24/18
* Time: 1:46 AM
*/
namespace A2nt\ElementalBasics\Extensions;
use Dynamic\Elements\Image\Elements\ElementImage;
use Sheadawson\Linkable\Forms\LinkField;
use Sheadawson\Linkable\Models\Link;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\ORM\DataExtension;
class ElementImageWidget extends DataExtension
{
private static $available_heights = [
'300' => 'Small (300px)',
'400' => 'Medium (400px)',
'600' => 'Big (600px)',
];
private static $available_widths = [
'300' => 'Small (300px)',
'400' => 'Medium (400px)',
'600' => 'Big (600px)',
];
private static $available_ratios = [
'1:1' => '1:1',
'3:2' => '3:2',
'2:3' => '2:3',
'16:9' => '16:9'
];
private static $db = [
'Resize' => 'Boolean(1)',
'ManualWidth' => 'Boolean(0)',
'ImageHeight' => 'Float',
'ImageWidth' => 'Float',
'ImageAspectRatio' => 'Text',
'Content' => 'HTMLText',
];
private static $has_one = [
'ImageLink' => Link::class,
];
public function updateCMSFields(FieldList $fields)
{
parent::updateCMSFields($fields);
$fields->insertBefore(
'Image',
LinkField::create('ImageLinkID', 'Link')
);
$this->owner->ImageHeight = $this->getHeight();
$heights = Config::inst()->get(__CLASS__, 'available_heights');
$widths = Config::inst()->get(__CLASS__, 'available_widths');
$ratios = Config::inst()->get(__CLASS__, 'available_ratios');
$fields->replaceField('Resize', CheckboxField::create(
'Resize',
'Would you like to scale image?'
));
$fields->removeByName(['ManualWidth','ImageWidth','ImageAspectRatio',]);
if (count($heights)) {
$fields->replaceField(
'ImageHeight',
CompositeField::create(
CheckboxField::create(
'ManualAspectRatio',
'Set Aspect Ratio',
($this->owner->getField('ImageAspectRatio') ? '1' : '0')
),
DropdownField::create(
'ImageAspectRatio',
'Image Aspect Ratio (width:height)',
$ratios
)
->setEmptyString('(original)')
->displayIf('ManualAspectRatio')->isChecked()
->andIf('ManualWidth')->isNotChecked()
->end(),
DropdownField::create(
'ImageHeight',
'Image Height',
$heights,
$this->getHeight()
)
->setEmptyString('(auto)')
->displayIf('Resize')->isChecked()->end(),
CheckboxField::create('ManualWidth', 'Set Width Manually')
->displayIf('Resize')->isChecked()
->andIf('ManualAspectRatio')->isNotChecked()
->end(),
DropdownField::create(
'ImageWidth',
'Image Width',
$widths
)
->setEmptyString('(auto)')
->displayIf('ManualWidth')->isChecked()
->end()
)
);
} else {
$fields->dataFieldByName('ImageHeight')
->setValue($this->getHeight());
}
}
public function ImageResized()
{
$image = $this->owner->Image();
if (!$this->owner->Resize) {
return $image;
}
$width = $this->getWidth();
$height = $this->getHeight();
$ratio = $this->owner->getField('ImageAspectRatio');
if ($ratio) {
$v = explode(':', $this->owner->getField('ImageAspectRatio'));
$x = $v[0];
$y = $v[1];
if ($width > 0 && $width !== 'auto') {
$height = $width*$y/$x;
echo 'a1';
} elseif ($height && $height > 0) {
$width = $height*$x/$y;
echo 'a2';
}
var_dump($width);
var_dump($height);
}
if (!$width || $width === 'auto') {
return $height > 0
? $image->ScaleHeight($height)
: $image;
}
return $height > 0
? $image->FocusFill($width, $height)
: $image->ScaleWidth($width);
}
public function getWidth()
{
$obj = $this->owner;
return $obj->getField('ManualWidth') && $obj->getField('ImageWidth')
? $obj->getField('ImageWidth')
: $obj->getColumnWidthRecursive();
}
public function getHeight()
{
$height = $this->owner->getField('ImageHeight');
if ($height > 0) {
return $height;
}
$sibling = $this->owner->getSibling(false, [
'ImageHeight:GreaterThan' => '0'
]);
if ($sibling && $sibling->getField('ImageHeight')) {
return $sibling->getField('ImageHeight');
}
return 0;
}
}

303
src/Extensions/ElementRows.php Executable file
View File

@ -0,0 +1,303 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/23/18
* Time: 1:23 PM
*/
namespace A2nt\ElementalBasics\Extensions;
use DNADesign\Elemental\Models\BaseElement;
use DNADesign\ElementalList\Model\ElementList;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\FieldList;
use A2nt\ElementalBasics\Elements\AccordionElement;
class ElementRows extends DataExtension
{
private static $container_max_width = 1140;
private static $column_class = 'col-block col-md';
private static $container_styles = [
'container' => 'Fixed container',
'container-fluid' => 'Fluid Container',
];
private static $db = [
'ContainerType' => 'Varchar(254)',
//'SidebarOnly' => 'Boolean(0)',
'Size' => 'Enum("1,2,3,4,5,6,7,8,9,10,11,12,auto","auto")',
];
public function updateCMSFields(FieldList $fields)
{
$obj = $this->owner;
parent::updateCMSFields($fields);
$fields->removeByName(['AvailableGlobally', 'TopPageLocale', 'TopPageID']);
//$fields->removeByName('SidebarOnly');
// move available globaly to main tab
$tab = $fields->findOrMakeTab('Root.Main');
$tab->push(CheckboxField::create('AvailableGlobally'));
//$tab->push(CheckboxField::create('SidebarOnly', 'Hidden (Sidebar Only)'));
// container type
if ($this->isRoot()) {
$tab->push(DropdownField::create(
'ContainerType',
_t(__CLASS__.'.CONTAINERTYPE', 'Container Type'),
self::$container_styles
));
} else {
$fields->removeByName('ContainerType');
}
// site-specific
$fields->removeByName('ContainerType');
// column size
if ($this->isColumn()) {
$sizes = $obj->dbObject('Size');
$sizeDropdown = DropdownField::create(
'Size',
_t(
__CLASS__.'.SIZE',
'Column Width (max 12 cols)'
),
array_combine(
array_values($sizes->enumValues()),
[
'8.3% (1 of 12)',
'16.6% (2 of 12)',
'25% (3 of 12)',
'33% (4 of 12)',
'41.6% (5 of 12)',
'50% (6 of 12)',
'58.3% (7 of 12)',
'66.4% (8 of 12)',
'74.7% (9 of 12)',
'83% (10 of 12)',
'91.3% (11 of 12)',
'100% (12 of 12)',
'auto',
]
)
);
$tab->push($sizeDropdown);
// set default size if size field wasn't set or if it's new object without title set and the size is default
$size = $obj->getField('Size');
if (!$size || (!$obj->getField('Title') && $size === 'auto')) {
$sibling = $this->getSibling();
$defaultSize = $sizes->getDefaultValue();
$defaultSize = $sibling ? $sibling->getField('Size') : $defaultSize;
$obj->setField('Size', $defaultSize);
$sizeDropdown->setValue($defaultSize);
}
} else {
$fields->removeByName('Size');
}
// move parent elements
if ($this->isList()) {
$currEls = $obj->getField('Elements')->Elements();
if ($currEls->count()) {
$tab->push(DropdownField::create(
'MoveElementIDToParent',
'Move an element from the current list to parent',
$currEls->map('ID', 'Title')
)->setEmptyString('(select an element to move)'));
}
$parentEls = $obj->Parent()->Elements()->exclude('ID', $obj->ID);
if ($parentEls->count()) {
$tab->push(DropdownField::create(
'MoveElementIDFromParent',
'Move an element from the parent to the current list',
$parentEls->map('ID', 'Title')
)->setEmptyString('(select an element to move)'));
}
}
}
public function getWidthPercetage()
{
return $this->isColumn() ? $this->owner->getField('Size') / self::colsNumber() * 100 : false;
}
public function isList()
{
return is_a($this->owner, ElementList::class) && !is_a($this->owner, AccordionElement::class);
}
public function isRow()
{
if (!$this->isList()) {
return false;
}
return true;
}
public function isColumn()
{
if (!$this->isRoot()) {
return true;
}
return false;
}
public function isRoot()
{
$parent = $this->owner->Parent()->getOwnerPage();
if (is_a($parent, 'Page')) {
return true;
}
return false;
}
public function getSibling($any = true, $filter = [], $exclude = [])
{
$class = $any ? $this->owner->baseClass() : $this->owner->ClassName;
return $class::get()->filter(array_merge(
['ParentID' => $this->owner->Parent()->ID],
$filter
))->exclude(array_merge(
['ID' => $this->owner->ID],
$exclude
))->last();
}
public function getColumnSizeRecursive($object = null)
{
$object = $object ? $object : $this->owner;
if ($object->isColumn() && $object->getField('Size')) {
return $object->getField('Size');
}
$parent = $object->Parent()->getOwnerPage();
if (is_a($parent, 'Page')) {
return ($this->owner->getField('ContainerType') === 'container-fluid') ? false : self::colsNumber();
}
return $object->getColumnSizeRecursive($parent);
}
public function getColumnWidthRecursive($object = null, $max = null)
{
$max = $max ? $max : self::maxWidth();
$object = $object ? $object : $this->owner;
if (!$object->isRoot()) {
$size = $object->getField('Size');
$cols = self::colsNumber();
if ($size === 'auto') {
return $size;
}
$max = $size ? $max / ($cols / $size) : $max;
$parent = $object->Parent()->getOwnerPage();
return $this->getColumnWidthRecursive($parent, $max);
}
return $max;
}
public static function colsNumber()
{
$db = Config::inst()->get(self::class, 'db');
$sizes = $db['Size'];
$sizes = preg_replace('!Enum\("([0-9A-z,]+)","([0-9A-z]+)"\)!i', '$1', $sizes);
$sizes = explode(',', $sizes);
// remove auto
$k = array_search('auto', $sizes);
if ($k !== false) {
unset($sizes[$k]);
}
return max($sizes);
}
public static function maxWidth()
{
return Config::inst()->get(self::class, 'container_max_width');
}
public function ExtraClass()
{
$object = $this->owner;
return $object->getField('ExtraClass')
.(
$this->isColumn()
? ' '.Config::inst()->get(self::class, 'column_class')
.($object->getField('Size') === 'auto' ? '' : '-'.$object->getField('Size'))
: ''
);
}
/*
* if it's root element and it doesn't contain any container styles
* add the first one
*/
public function ContainerClass()
{
$type = $this->owner->getField('ContainerType');
$container_styles = array_keys(self::$container_styles);
if (!$type && $this->isRoot()) {
$type = \Page::DefaultContainer();
}
return $type;
}
public static function MoveElement($moveID, $targetID)
{
$el = BaseElement::get_by_id($moveID);
if (!$el) {
return false;
}
$el->setField('ParentID', $targetID);
$el->write();
return true;
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
$moveID = $this->owner->getField('MoveElementIDFromParent');
$targetID = $moveID ? $this->owner->Elements()->ID : null;
if ($moveID && $targetID) {
self::MoveElement($moveID, $targetID);
}
$moveID = $this->owner->getField('MoveElementIDToParent');
$targetID = $moveID ? $this->owner->Parent()->ID : null;
if ($moveID && $targetID) {
self::MoveElement($moveID, $targetID);
}
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/23/18
* Time: 1:23 PM
*/
namespace A2nt\ElementalBasics\Extensions;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\HasManyList;
use SilverStripe\ORM\UnsavedRelationList;
class ElementalArea extends DataExtension
{
public function ElementFilteredControllers()
{
// Don't try and process unsaved lists
if ($this->Elements() instanceof UnsavedRelationList) {
return ArrayList::create();
}
$controllers = ArrayList::create();
$items = $this->Elements()->filterByCallback(static function (BaseElement $item) {
return $item->canView();
});
if ($items !== null) {
foreach ($items as $element) {
$controller = $element->getController();
$controllers->push($controller);
}
}
return $controllers;
}
/**
* A cache-aware accessor for the elements
* @return ArrayList|HasManyList|BaseElement[]
*/
public function Elements()
{
return $this->owner->Elements();//->exclude('SidebarOnly', true);
}
}

73
src/Extensions/MapExtension.php Executable file
View File

@ -0,0 +1,73 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 8/26/18
* Time: 12:55 PM
*/
namespace A2nt\ElementalBasics\Extensions;
use SilverStripe\Core\Extension;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataExtension;
use A2nt\ElementalBasics\Models\MapPin;
class MapExtension extends DataExtension
{
private static $db = [
'MapZoom' => 'Int',
];
private static $many_many = [
'Locations' => MapPin::class,
];
private static $owns = [
'Locations',
];
public function updateCMSFields(FieldList $fields): void
{
parent::updateCMSFields($fields);
$fields->removeByName('Locations');
$fields->addFieldsToTab('Root.MapPins', [
NumericField::create('MapZoom', 'Initial Map Zoom (enter a number from 0 to 24)'),
GridField::create(
'Locations',
'Locations',
$this->owner->Locations(),
$cfg = GridFieldConfig_RelationEditor::create(100)
)
]);
$cfg->getComponentByType(GridFieldDataColumns::class)->setFieldFormatting([
'ShowAtMap' => static function ($v, $obj) {
return $v ? 'YES' : 'NO';
}
]);
$fields->findOrMakeTab('Root.MapPins')->setTitle('Locations');
}
public function getGeoJSON(): string
{
$locs = $this->owner->Locations()->filter('ShowAtMap', true);
$pins = [];
foreach ($locs as $off) {
$pins[] = $off->getGeo();
}
return json_encode([
'type' => 'MarkerCollection',
'features' => $pins
]);
}
}

View File

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

144
src/Models/MapPin.php Executable file
View File

@ -0,0 +1,144 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 9/12/18
* Time: 2:55 AM
*/
namespace A2nt\ElementalBasics\Models;
use A2nt\SilverStripeMapboxField\MapboxField;
use A2nt\SilverStripeMapboxField\MarkerExtension;
use Sheadawson\Linkable\Forms\LinkField;
use Sheadawson\Linkable\Models\Link;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
use A2nt\ElementalBasics\Elements\MapElement;
use Symbiote\Addressable\Addressable;
class MapPin extends DataObject
{
private static $table_name = 'MapPin';
private static $db = [
'Title' => 'Varchar(255)',
'ShowAtMap' => 'Boolean(1)',
];
private static $has_one = [
'PhoneNumber' => Link::class,
'Fax' => Link::class,
];
private static $extensions = [
Addressable::class,
MarkerExtension::class,
Versioned::class,
];
private static $belongs_many_many = [
'MapElements' => MapElement::class,
];
private static $default_sort = 'Title ASC, ID DESC';
private static $summary_fields = [
'Title',
'Address',
'ShowAtMap',
];
private static $defaults = [
'ShowAtMap' => '1',
'Suburb' => 'Syracuse',
'State' => 'NY',
'Postcode' => '13210',
'Country' => 'US',
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->removeByName('MapElements');
$fields->replaceField(
'PhoneNumberID',
LinkField::create('PhoneNumberID', 'Phone Number')
->setAllowedTypes(['Phone'])
);
$fields->replaceField(
'FaxID',
LinkField::create('FaxID', 'FAX')
->setAllowedTypes(['Phone'])
);
$fields->removeByName(['Map', 'LatLngOverride', 'Lng','Lat']);
$fields->addFieldsToTab('Root.Main', [
CheckboxField::create('ShowAtMap', 'Show at the map?'),
CheckboxField::create('LatLngOverride', 'Override Latitude and Longitude?')
->setDescription('Check this box and save to be able to edit the latitude and longitude manually.'),
MapboxField::create('Map', 'Choose a location', 'Lat', 'Lng'),
]);
return $fields;
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
$lng = $this->getField('Lng');
$lat = $this->getField('Lat');
if (!$this->getField('Country')) {
$this->setField('Country', 'us');
}
// geocode
try {
// reverse geocoding get address
if (!$this->hasAddress() && $lng && $lat) {
require_once BASE_PATH . '/app/thirdparty/geocoding-example/php/Mapbox.php';
$mapbox = new \Mapbox(MapboxField::getAccessToken());
// GET Address
$res = $mapbox->reverseGeocode($lng, $lat);
if ($res->success() && $res->getCount()) {
$res = $res->getData();
if (count($res) && isset($res[0]['place_name'])) {
$details = explode(',', $res[0]['place_name']);
$fields = [
'Address',
'City',
'State',
//'Country',
];
$n = count($fields);
for ($i = 0; $i < $n; $i++) {
if (!isset($details[$i])) {
continue;
}
$name = $fields[$i];
$val = $details[$i];
// get postal code
if ($name === 'State') {
$this->setField('PostalCode', substr($val, strrpos($val, ' ')+1));
}
$this->setField($name, $val);
}
}
}
}
} catch (\Exception $e) {
}
}
}

63
src/Models/TeamMember.php Executable file
View File

@ -0,0 +1,63 @@
<?php
/**
* Created by PhpStorm.
* User: tony
* Date: 6/30/18
* Time: 11:36 PM
*/
namespace A2nt\ElementalBasics\Models;
use SilverStripe\Assets\Image;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
use App\Extensions\SocialExtension;
class TeamMember extends DataObject
{
private static $table_name = 'TeamMember';
private static $db = [
'FirstName' => 'Varchar(254)',
'LastName' => 'Varchar(254)',
'Company' => 'Varchar(254)',
'Position' => 'Varchar(254)',
'Content' => 'HTMLText',
];
private static $has_one = [
'Photo' => Image::class,
];
private static $extensions = [
SocialExtension::class,
Versioned::class,
];
private static $owns = [
'Photo',
];
private static $summary_fields = [
'Company',
'FirstName',
'LastName',
'Position',
];
private static $searchable_fields = [
'FirstName',
'LastName',
];
private static $frontend_searchable_fields = [
'FirstName:PartialMatch',
'LastName:PartialMatch',
'Content:PartialMatch',
];
public function getTitle()
{
return $this->getField('Company').' | '.$this->getField('FirstName').' '.$this->getField('LastName');
}
}