API Remove widgets

This commit is contained in:
Steve Boyd 2024-08-22 14:25:48 +12:00
parent 1bdb661705
commit 5e44aa7595
26 changed files with 28 additions and 1233 deletions

View File

@ -1 +1 @@
.no-sidebar .content-container.size3of4{width:75%}.blog-entry .post-image img{width:98.75%}ul.blogTagCloud{list-style-type:none;clear:both}ul.blogTagCloud li{float:left;display:inline;padding-right:8px}ul.blogTagCloud li a span{float:left;line-height:30px;text-align:center;padding:0}ul.blogTagCloud .blog-sidebar .WidgetHolder ul{margin-left:0}ul.blogTagCloud .blog-sidebar .WidgetHolder ul li{list-style-type:none}ul.blogTagCloud .tagCount10{font-size:26pt}ul.blogTagCloud .tagCount9{font-size:24pt}ul.blogTagCloud .tagCount8{font-size:22pt}ul.blogTagCloud .tagCount7{font-size:20pt}ul.blogTagCloud .tagCount6{font-size:18pt}ul.blogTagCloud .tagCount5{font-size:16pt}ul.blogTagCloud .tagCount4{font-size:14pt}ul.blogTagCloud .tagCount3{font-size:12pt}ul.blogTagCloud .tagCount2{font-size:10pt}ul.blogTagCloud .tagCount1{font-size:8pt}.cms table.ss-gridfield-table tr td.MergeAction a{display:block;height:100%;width:100%}.has-panel .cms-content-tools.blog-admin-sidebar{width:280px;border-right:0;border-left:1px solid #c0c0c2;position:absolute !important;right:0;top:0;height:100%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle a{text-align:left;margin:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle.south{border-top:1px solid #aaa}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer{width:100%;padding-right:280px;position:absolute;height:100%;overflow:hidden hidden;box-sizing:border-box}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset{position:relative;overflow:auto;height:100%;width:100%}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title label{float:none}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title input{width:100%;max-width:100%;margin-left:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field+.field{margin-top:10px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .preview{padding-top:0;line-height:25px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .edit{float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.date{width:60%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.time{width:36%;float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn input{width:100%}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer{padding-right:41px}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer #Root_Main{margin-right:15px}.has-panel .cms-content-tools.blog-admin-sidebar.cms-content-tools .cms-panel-content{width:auto}#FeaturedImage .middleColumn{clear:none;float:left}.toggle-description{display:inline-block;font-size:1.2rem;width:20px;height:20px;margin-top:1px;cursor:pointer}.middleColumn.toggle-description-correct-middle{margin-left:0;float:left;width:416px}.tab-content .field p.toggle-description-correct-right{display:inline-block;margin-left:0;padding-left:0;clear:none;float:left}.description.toggle-description-correct-description{width:416px;padding:12px 0}.custom-summary .ui-accordion-content,.custom-summary .ui-accordion-content .field{padding:0}.cms table.ss-gridfield-table tr td.MergeAction{width:225px}.cms table.ss-gridfield-table tr td.MergeAction select{width:150px}.blog-cms-categorisation .toolbar--content{margin-top:0}.blog-cms-categorisation .MergeActionReveal:after{content:"@";font-family:"silverstripe";display:inline-block;position:relative;margin-left:10px;top:3px}.blog-cms-categorisation .blog-merge-action{margin-top:5px}
.no-sidebar .content-container.size3of4{width:75%}.blog-entry .post-image img{width:98.75%}ul.blogTagCloud{list-style-type:none;clear:both}ul.blogTagCloud li{float:left;display:inline;padding-right:8px}ul.blogTagCloud li a span{float:left;line-height:30px;text-align:center;padding:0}ul.blogTagCloud .tagCount10{font-size:26pt}ul.blogTagCloud .tagCount9{font-size:24pt}ul.blogTagCloud .tagCount8{font-size:22pt}ul.blogTagCloud .tagCount7{font-size:20pt}ul.blogTagCloud .tagCount6{font-size:18pt}ul.blogTagCloud .tagCount5{font-size:16pt}ul.blogTagCloud .tagCount4{font-size:14pt}ul.blogTagCloud .tagCount3{font-size:12pt}ul.blogTagCloud .tagCount2{font-size:10pt}ul.blogTagCloud .tagCount1{font-size:8pt}.cms table.ss-gridfield-table tr td.MergeAction a{display:block;height:100%;width:100%}.has-panel .cms-content-tools.blog-admin-sidebar{width:280px;border-right:0;border-left:1px solid #c0c0c2;position:absolute !important;right:0;top:0;height:100%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle a{text-align:left;margin:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle.south{border-top:1px solid #aaa}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer{width:100%;padding-right:280px;position:absolute;height:100%;overflow:hidden hidden;box-sizing:border-box}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset{position:relative;overflow:auto;height:100%;width:100%}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title label{float:none}.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar~.blog-admin-outer>.ss-tabset #Title input{width:100%;max-width:100%;margin-left:0}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field+.field{margin-top:10px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .preview{padding-top:0;line-height:25px}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.urlsegment .edit{float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.date{width:60%}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn>.time{width:36%;float:right}.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn .middleColumn,.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view>.field.datetime>.middleColumn input{width:100%}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer{padding-right:41px}.has-panel .cms-content-tools.blog-admin-sidebar.collapsed~.blog-admin-outer #Root_Main{margin-right:15px}.has-panel .cms-content-tools.blog-admin-sidebar.cms-content-tools .cms-panel-content{width:auto}#FeaturedImage .middleColumn{clear:none;float:left}.toggle-description{display:inline-block;font-size:1.2rem;width:20px;height:20px;margin-top:1px;cursor:pointer}.middleColumn.toggle-description-correct-middle{margin-left:0;float:left;width:416px}.tab-content .field p.toggle-description-correct-right{display:inline-block;margin-left:0;padding-left:0;clear:none;float:left}.description.toggle-description-correct-description{width:416px;padding:12px 0}.custom-summary .ui-accordion-content,.custom-summary .ui-accordion-content .field{padding:0}.cms table.ss-gridfield-table tr td.MergeAction{width:225px}.cms table.ss-gridfield-table tr td.MergeAction select{width:150px}.blog-cms-categorisation .toolbar--content{margin-top:0}.blog-cms-categorisation .MergeActionReveal:after{content:"@";font-family:"silverstripe";display:inline-block;position:relative;margin-left:10px;top:3px}.blog-cms-categorisation .blog-merge-action{margin-top:5px}

View File

@ -26,14 +26,6 @@ ul.blogTagCloud {
}
}
.blog-sidebar .WidgetHolder ul {
margin-left: 0;
li {
list-style-type: none;
}
}
.tagCount10 {
font-size: $base-tag-font-size + 22pt;
}

View File

@ -1,48 +0,0 @@
<?php
namespace SilverStripe\Blog\Model;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Dev\Deprecation;
/**
* Adds a checkbox field for featured blog posts widget.
*
* @extends DataExtension<BlogPost>
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogPostFeaturedExtension extends DataExtension
{
public function __construct()
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct();
}
/**
* @var array
*/
private static $db = [
'FeaturedInWidget' => 'Boolean',
];
/**
* {@inheritdoc}
*/
protected function updateCMSFields(FieldList $fields)
{
// Add the checkbox in.
$fields->addFieldToTab(
'Root.PostOptions',
CheckboxField::create('FeaturedInWidget', _t(__CLASS__ . '.FEATURED', 'Include Post in Feature Widget'))
);
}
}

View File

@ -1,175 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBDate;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBEnum;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ArrayData;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\DataObject;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
*
* @property string $ArchiveType
* @property int $NumberToDisplay
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogArchiveWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Archive';
/**
* @var string
*/
private static $cmsTitle = 'Archive';
/**
* @var string
*/
private static $description = 'Displays an archive list of posts.';
/**
* @var array
*/
private static $db = [
'NumberToDisplay' => 'Int',
'ArchiveType' => 'Enum(\'Monthly,Yearly\', \'Monthly\')',
];
/**
* @var array
*/
private static $defaults = [
'NumberOfMonths' => 12,
];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class,
];
/**
* @var string
*/
private static $table_name = 'BlogArchiveWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
/**
* @var DBEnum $archiveType
*/
$archiveType = $this->dbObject('ArchiveType');
$type = $archiveType->enumValues();
foreach ($type as $k => $v) {
/** @phpstan-ignore translation.key (we need the key to be dynamic here) */
$type[$k] = _t(__CLASS__ .'.' . ucfirst(strtolower($v ?? '')), $v);
}
/**
* @var FieldList $fields
*/
$fields->merge([
DropdownField::create(
'BlogID',
_t(__CLASS__ . '.Blog', 'Blog'),
Blog::get()->map()
),
DropdownField::create('ArchiveType', _t(__CLASS__ . '.ArchiveType', 'ArchiveType'), $type),
NumericField::create('NumberToDisplay', _t(__CLASS__ . '.NumberToDisplay', 'No. to Display'))
]);
});
return parent::getCMSFields();
}
/**
* Returns a list of months where blog posts are present.
*
* @return ArrayList<ArrayData>
*/
public function getArchive()
{
$format = ($this->ArchiveType == 'Yearly') ? '%Y' : '%Y-%m';
$publishDate = DB::get_conn()->formattedDatetimeClause('"PublishDate"', $format);
$fields = [
'PublishDate' => $publishDate,
'Total' => "COUNT('\"PublishDate\"')"
];
$stage = Versioned::get_stage();
$suffix = ($stage === Versioned::LIVE) ? '_' . Versioned::LIVE : '';
$query = SQLSelect::create($fields, '"BlogPost' . $suffix . '"')
->addGroupBy($publishDate)
->addOrderBy('"PublishDate" DESC')
->addLeftJoin('SiteTree' . $suffix, '"SiteTree' . $suffix . '"."ID" = "BlogPost' . $suffix . '"."ID"')
->addWhere([
'"PublishDate" <= ?' => DBDatetime::now()->Format(DBDatetime::ISO_DATETIME),
'"SiteTree' . $suffix . '"."ParentID"' => $this->BlogID,
]);
$posts = $query->execute();
$result = ArrayList::create();
foreach ($posts as $post) {
if ($this->ArchiveType == 'Yearly') {
$year = $post['PublishDate'];
$month = null;
$title = $year;
} else {
$date = DBDate::create();
$date->setValue(strtotime($post['PublishDate'] ?? ''));
$year = $date->Format('y');
$month = $date->Format('MM');
$title = $date->Format('MMMM y');
}
$result->push(ArrayData::create([
'Title' => $title,
'Link' => Controller::join_links($this->Blog()->Link('archive'), $year, $month)
]));
}
$this->extend('updateGetArchive', $result);
return $result;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Widgets\Model\WidgetController;
use SilverStripe\Dev\Deprecation;
if (!class_exists(WidgetController::class)) {
return;
}
/**
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogArchiveWidgetController extends WidgetController
{
public function __construct($widget = null)
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($widget);
}
}

View File

@ -1,147 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Blog\Model\BlogCategory;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataList;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\DataObject;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogCategoriesWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Categories';
/**
* @var string
*/
private static $cmsTitle = 'Blog Categories';
/**
* @var string
*/
private static $description = 'Displays a list of blog categories.';
/**
* @var array
*/
private static $db = [
'Limit' => 'Int',
'Order' => 'Varchar',
'Direction' => 'Varchar',
];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class,
];
/**
* @var string
*/
private static $table_name = 'BlogCategoriesWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function (FieldList $fields) {
$fields[] = DropdownField::create(
'BlogID',
_t(__CLASS__ . '.Blog', 'Blog'),
Blog::get()->map()
);
$fields[] = NumericField::create(
'Limit',
_t(__CLASS__ . '.Limit', 'Limit'),
0
)
->setDescription(
_t(
__CLASS__ . '.Limit_Description',
'Limit the number of categories shown by this widget (set to 0 to show all categories).'
)
)
->setMaxLength(3);
$fields[] = DropdownField::create(
'Order',
_t(__CLASS__ . '.Sort', 'Sort'),
['Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated']
)
->setDescription(
_t(__CLASS__ . '.Sort_Description', 'Change the order of categories shown by this widget.')
);
$fields[] = DropdownField::create(
'Direction',
_t(__CLASS__ . '.Direction', 'Direction'),
['ASC' => 'Ascending', 'DESC' => 'Descending']
)
->setDescription(
_t(
__CLASS__ . '.Direction_Description',
'Change the direction of ordering of categories shown by this widget.'
)
);
});
return parent::getCMSFields();
}
/**
* @return DataList<BlogCategory>
*/
public function getCategories()
{
$blog = $this->Blog();
if (!$blog) {
return [];
}
$query = $blog->Categories();
if ($this->Limit) {
$query = $query->limit(Convert::raw2sql($this->Limit));
}
if ($this->Order && $this->Direction) {
$query = $query->sort(Convert::raw2sql($this->Order), Convert::raw2sql($this->Direction));
}
return $query;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Widgets\Model\WidgetController;
use SilverStripe\Dev\Deprecation;
if (!class_exists(WidgetController::class)) {
return;
}
/**
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogCategoriesWidgetController extends WidgetController
{
public function __construct($widget = null)
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($widget);
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Control\Director;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataList;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\DataObject;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
*
* @property int $NumberOfPosts
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogFeaturedPostsWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Featured Posts';
/**
* @var string
*/
private static $cmsTitle = 'Featured Posts';
/**
* @var string
*/
private static $description = 'Displays a list of featured blog posts.';
/**
* @var array
*/
private static $db = [
'NumberOfPosts' => 'Int',
];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class,
];
/**
* @var string
*/
private static $table_name = 'BlogFeaturedPostsWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
/**
* @var FieldList $fields
*/
$fields->merge([
DropdownField::create('BlogID', _t(__CLASS__ . '.Blog', 'Blog'), Blog::get()->map()),
NumericField::create('NumberOfPosts', _t(__CLASS__ . '.NumberOfPosts', 'Number of Posts'))
]);
});
return parent::getCMSFields();
}
/**
* @return array|DataList
*/
public function getPosts()
{
$blog = $this->Blog();
if ($blog) {
return $blog->getBlogPosts()
->filter('ID:not', Director::get_current_page()->ID)
->filter('FeaturedInWidget', true)
->shuffle()
->limit($this->NumberOfPosts);
}
return [];
}
}

View File

@ -1,107 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Control\Director;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataList;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\ORM\DataObject;
use SilverStripe\Dev\Deprecation;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
*
* @property int $NumberOfPosts
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogRecentPostsWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Recent Posts';
/**
* @var string
*/
private static $cmsTitle = 'Recent Posts';
/**
* @var string
*/
private static $description = 'Displays a list of recent blog posts.';
/**
* @var array
*/
private static $db = [
'NumberOfPosts' => 'Int',
];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class,
];
/**
* @var string
*/
private static $table_name = 'BlogRecentPostsWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
/**
* @var FieldList $fields
*/
$fields->merge([
DropdownField::create('BlogID', _t(__CLASS__ . '.Blog', 'Blog'), Blog::get()->map()),
NumericField::create('NumberOfPosts', _t(__CLASS__ . '.NumberOfPosts', 'Number of Posts'))
]);
});
return parent::getCMSFields();
}
/**
* @return array|DataList
*/
public function getPosts()
{
$blog = $this->Blog();
if ($blog) {
return $blog->getBlogPosts()
->filter('ID:not', Director::get_current_page()->ID)
->sort('"PublishDate" DESC')
->limit($this->NumberOfPosts);
}
return [];
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Widgets\Model\WidgetController;
use SilverStripe\Dev\Deprecation;
if (!class_exists(WidgetController::class)) {
return;
}
/**
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogRecentPostsWidgetController extends WidgetController
{
public function __construct($widget = null)
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($widget);
}
}

View File

@ -1,140 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\Control\Controller;
use SilverStripe\Dev\Deprecation;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogTagsCloudWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Tags Cloud';
/**
* @var string
*/
private static $cmsTitle = 'Blog Tags Cloud';
/**
* @var string
*/
private static $description = 'Displays a tag cloud for this blog.';
/**
* @var array
*/
private static $db = [];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class,
];
/**
* @var string
*/
private static $table_name = 'BlogTagsCloudWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
/*
* @var FieldList $fields
*/
$fields->push(
DropdownField::create(
'BlogID',
_t(__CLASS__ . '.Blog', 'Blog'),
Blog::get()->map()
)
);
});
return parent::getCMSFields();
}
/**
* @return array
*/
public function getTags()
{
if ($blog = $this->Blog()) {
$escapedID = Convert::raw2sql($blog->ID);
$sql = 'SELECT DISTINCT "BlogTag"."URLSegment","BlogTag"."Title",Count("BlogTagID") AS "TagCount"
from "BlogPost_Tags"
INNER JOIN "BlogPost"
ON "BlogPost"."ID" = "BlogPost_Tags"."BlogPostID"
INNER JOIN "BlogTag"
ON "BlogTag"."ID" = "BlogPost_Tags"."BlogTagID"
WHERE "BlogID" = ' . $escapedID
. ' GROUP By "BlogTag"."URLSegment","BlogTag"."Title"
ORDER BY "Title"';
$records = DB::query($sql);
$bloglink = $blog->Link();
$maxTagCount = 0;
// create DataObjects that can be used to render the tag cloud
$tags = ArrayList::create();
foreach ($records as $record) {
$tag = DataObject::create();
$tag->TagName = $record['Title'];
$link = Controller::join_links($bloglink, 'tag', $record['URLSegment']);
$tag->Link = $link;
if ($record['TagCount'] > $maxTagCount) {
$maxTagCount = $record['TagCount'];
}
$tag->TagCount = $record['TagCount'];
$tags->push($tag);
}
// normalize the tag counts from 1 to 10
if ($maxTagCount) {
$tagfactor = 10 / $maxTagCount;
foreach ($tags->getIterator() as $tag) {
$normalized = round($tagfactor * ($tag->TagCount));
$tag->NormalizedTag = $normalized;
}
}
return $tags;
}
return [];
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Widgets\Model\WidgetController;
use SilverStripe\Dev\Deprecation;
if (!class_exists(WidgetController::class)) {
return;
}
/**
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogTagsCloudWidgetController extends WidgetController
{
public function __construct($widget = null)
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($widget);
}
}

View File

@ -1,147 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Blog\Model\Blog;
use SilverStripe\Blog\Model\BlogTag;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataList;
use SilverStripe\Widgets\Model\Widget;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\DataObject;
if (!class_exists(Widget::class)) {
return;
}
/**
* @method Blog Blog()
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogTagsWidget extends Widget
{
/**
* @var string
*/
private static $title = 'Tags';
/**
* @var string
*/
private static $cmsTitle = 'Blog Tags';
/**
* @var string
*/
private static $description = 'Displays a list of blog tags.';
/**
* @var array
*/
private static $db = [
'Limit' => 'Int',
'Order' => 'Varchar',
'Direction' => 'Varchar',
];
/**
* @var array
*/
private static $has_one = [
'Blog' => Blog::class
];
/**
* @var string
*/
private static $table_name = 'BlogTagsWidget';
public function __construct($record = [], $creationType = DataObject::CREATE_OBJECT, $queryParams = [])
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($record, $creationType, $queryParams);
}
/**
* {@inheritdoc}
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function (Fieldlist $fields) {
$fields[] = DropdownField::create(
'BlogID',
_t(__CLASS__ . '.Blog', 'Blog'),
Blog::get()->map()
);
$fields[] = NumericField::create(
'Limit',
_t(__CLASS__ . '.Limit', 'Limit'),
0
)
->setDescription(
_t(
__CLASS__ . '.Limit_Description',
'Limit the number of tags shown by this widget (set to 0 to show all tags).'
)
)
->setMaxLength(3);
$fields[] = DropdownField::create(
'Order',
_t(__CLASS__ . '.Sort', 'Sort'),
['Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated']
)
->setDescription(
_t(__CLASS__ . '.Sort_Description', 'Change the order of tags shown by this widget.')
);
$fields[] = DropdownField::create(
'Direction',
_t(__CLASS__ . '.Direction', 'Direction'),
['ASC' => 'Ascending', 'DESC' => 'Descending']
)
->setDescription(
_t(
__CLASS__ . '.Direction_Description',
'Change the direction of ordering of tags shown by this widget.'
)
);
});
return parent::getCMSFields();
}
/**
* @return DataList<BlogTag>
*/
public function getTags()
{
$blog = $this->Blog();
if (!$blog) {
return [];
}
$query = $blog->Tags();
if ($this->Limit) {
$query = $query->limit(Convert::raw2sql($this->Limit));
}
if ($this->Order && $this->Direction) {
$query = $query->sort(Convert::raw2sql($this->Order), Convert::raw2sql($this->Direction));
}
return $query;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace SilverStripe\Blog\Widgets;
use SilverStripe\Widgets\Model\WidgetController;
use SilverStripe\Dev\Deprecation;
if (!class_exists(WidgetController::class)) {
return;
}
/**
* @deprecated 4.3.0 Will be removed without equivalent functionality to replace it
*/
class BlogTagsWidgetController extends WidgetController
{
public function __construct($widget = null)
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'4.3.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct($widget);
}
}

View File

@ -1,12 +0,0 @@
<% if $Archive %>
<ul>
<% loop $Archive %>
<li>
<a href="$Link" title="$Title">
<span class="arrow">&rarr;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
<% end_if %>

View File

@ -1,12 +0,0 @@
<% if $Categories %>
<ul>
<% loop $Categories %>
<li>
<a href="$Link" title="$Title">
<span class="arrow">&rarr;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
<% end_if %>

View File

@ -1,12 +0,0 @@
<% if $Posts %>
<ul>
<% loop $Posts %>
<li>
<a href="$Link" title="$Title">
<span class="arrow">&rarr;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
<% end_if %>

View File

@ -1,12 +0,0 @@
<% if $Posts %>
<ul>
<% loop $Posts %>
<li>
<a href="$Link" title="$Title">
<span class="arrow">&rarr;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
<% end_if %>

View File

@ -1,12 +0,0 @@
<% if $Tags %>
<ul class="blogTagCloud">
<% loop $Tags %>
<li>
<a href="$Link" title="$TagName">
<span class="text tagCount{$NormalizedTag}">$TagName</span>
</a>
</li>
<% end_loop %>
</ul>
<div style="clear: both;"></div>
<% end_if %>

View File

@ -1,12 +0,0 @@
<% if $Tags %>
<ul>
<% loop $Tags %>
<li>
<a href="$Link" title="$Title">
<span class="arrow">&rarr;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
<% end_if %>

View File

@ -1,4 +0,0 @@
<nav class="secondary">
<% if $Title %><h3>$Title</h3><% end_if %>
$Content
</nav>

View File

@ -8,83 +8,6 @@ use PHPUnit\Framework\Assert;
class FeatureContext extends SilverStripeContext
{
/**
* Adds a widget to the blog
*
* @Then /^I add the "([^"]+)" widget$/
* @param $widgetTitle e.g. "Content"
*/
public function iAddTheWidget($widgetTitle)
{
$page = $this->getSession()->getPage();
$h3s = $page->findAll('css', '.availableWidgetsHolder h3');
$found = false;
foreach ($h3s as $h3) {
if ($h3->getText() !== $widgetTitle) {
continue;
}
$found = true;
$h3->click();
}
Assert::assertTrue($found, "Widget {$widgetTitle} was not found");
}
/**
* Fills in a field within a widget
*
* @Then /^I fill in the "([^"]+)" widget field "([^"]+)" with "([^"]+)"$/
* @param $widgetTitle e.g. "Content"
* @param $htmlFragment e.g. "Title"
* @param $value e.g. "Lorem ipsum"
*/
public function iFillInTheWidgetField($widgetTitle, $fieldTitle, $value)
{
$page = $this->getSession()->getPage();
$widget = $this->getWidget($widgetTitle);
$field = $widget->findField($fieldTitle);
Assert::assertNotNull($field, "Widget field {$fieldTitle} was not found");
$field->setValue($value);
}
/**
* Adapated from framework CmsFormsContext stepIFillInTheHtmlFieldWith
*
* @When /^I fill in the "([^"]+)" widget HTML field "([^"]+)" with "([^"]+)"$/
*/
public function stepIFillInTheHtmlFieldWith($widgetTitle, $fieldTitle, $value)
{
$widget = $this->getWidget($widgetTitle);
$field = $this->getDescendantHtmlField($widget, $fieldTitle);
$this->getSession()->evaluateScript(sprintf(
"jQuery('#%s').entwine('ss').getEditor().setContent('%s')",
$field->getAttribute('id'),
addcslashes($value ?? '', "'")
));
$this->getSession()->evaluateScript(sprintf(
"jQuery('#%s').entwine('ss').getEditor().save()",
$field->getAttribute('id')
));
}
/**
* @return NodeElement|null
*/
private function getWidget($widgetTitle)
{
$ret = null;
$widgets = $this->getSession()->getPage()->findAll('css', '.usedWidgets .Widget');
foreach ($widgets as $widget) {
$h3 = $widget->find('css', 'h3');
if (!$h3 || $h3->getText() !== $widgetTitle) {
continue;
}
$ret = $widget;
break;
}
Assert::assertNotNull($ret, "Widget edit form for {$widgetTitle} was not found");
return $ret;
}
/**
* @param NodeElement $ancestor
* @param string $locator

View File

@ -59,7 +59,7 @@ class BlogCategoryTest extends FunctionalTest
*/
$category = $this->objFromFixture(BlogCategory::class, 'FirstCategory');
$this->assertEquals(5, $category->BlogPosts()->count(), 'Category blog post count');
$this->assertEquals(2, $category->BlogPosts()->count(), 'Category blog post count');
}
/**

View File

@ -48,10 +48,10 @@ class BlogPostFilterTest extends SapphireTest
*/
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
$this->assertEquals(3, $blog->AllChildren()->Count(), 'Filtered blog posts');
$this->assertEquals(4, $blog->AllChildren()->Count(), 'Filtered blog posts');
DBDatetime::set_mock_now('2020-01-01 00:00:00');
$this->assertEquals(5, $blog->AllChildren()->Count(), 'Unfiltered blog posts');
$this->assertEquals(6, $blog->AllChildren()->Count(), 'Unfiltered blog posts');
}
}

View File

@ -117,24 +117,6 @@ SilverStripe\Blog\Model\BlogTag:
URLSegment: 'third-tag'
BlogID: =>SilverStripe\Blog\Model\Blog.ThirdBlog
#Tags for Tag Cloud widget
PopularTag:
Title: 'Popular'
URLSegment: 'popular'
BlogID: =>SilverStripe\Blog\Model\Blog.FourthBlog
CoolTag:
Title: 'Cool'
URLSegment: 'cool'
BlogID: =>SilverStripe\Blog\Model\Blog.FourthBlog
CatTag:
Title: 'Cat'
URLSegment: 'cat'
BlogID: =>SilverStripe\Blog\Model\Blog.FourthBlog
KiwiTag:
Title: 'Kiwi'
URLSegment: 'kiwi'
BlogID: =>SilverStripe\Blog\Model\Blog.FourthBlog
SilverStripe\Blog\Model\BlogPost:
FirstBlogPost:
ClassName: SilverStripe\Blog\Model\BlogPost
@ -167,6 +149,13 @@ SilverStripe\Blog\Model\BlogPost:
PublishDate: '2012-01-09 15:00:00'
Content: >
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas nec accumsan dolor, at viverra nibh. Vivamus lectus magna, interdum sit amet libero non, fermentum semper nunc. Nunc eu arcu euismod mauris auctor venenatis. Integer sem lorem, varius iaculis congue et, blandit eget nisi. Quisque ullamcorper laoreet sagittis. Nulla enim metus, viverra ut commodo ut, imperdiet et velit. Sed tincidunt mi condimentum, sodales lectus id, dictum neque. Suspendisse placerat, nibh at mattis volutpat, mauris augue congue dui, lobortis tempor diam magna vitae ligula. Proin sed turpis quis enim maximus sodales sit amet id diam. Proin sollicitudin elementum suscipit.</p>
AnotherPostWithFirstCategory:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Another post with first category'
URLSegment: another-post-with-first-category
ParentID: =>SilverStripe\Blog\Model\Blog.FirstBlog
PublishDate: '2012-01-09 15:00:00'
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
FirstFutureBlogPost:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Future Post'
@ -207,39 +196,3 @@ SilverStripe\Blog\Model\BlogPost:
ParentID: =>SilverStripe\Blog\Model\Blog.FourthBlog
Authors: =>SilverStripe\Security\Member.Writer,=>SilverStripe\Security\Member.Contributor,=>SilverStripe\Security\Member.BlogEditor
PublishDate: null
#Posts for the tag cloud widget test
TaggedPost1:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Tagged Post 1'
URLSegment: tagged-post-1
ParentID: =>SilverStripe\Blog\Model\Blog.FourthBlog
PublishDate: '2012-01-09 15:00:00'
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
TaggedPost2:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Tagged Post 2'
URLSegment: tagged-post-2
ParentID: =>SilverStripe\Blog\Model\Blog.FourthBlog
PublishDate: '2012-01-09 15:00:00'
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag,=>SilverStripe\Blog\Model\BlogTag.CatTag
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
TaggedPost3:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Tagged Post 3'
URLSegment: tagged-post-3
ParentID: =>SilverStripe\Blog\Model\Blog.FourthBlog
PublishDate: '2012-01-09 17:20:00'
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag,=>SilverStripe\Blog\Model\BlogTag.CatTag,=>SilverStripe\Blog\Model\BlogTag.KiwiTag
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
TaggedPost4:
ClassName: SilverStripe\Blog\Model\BlogPost
Title: 'Tagged Post 4'
URLSegment: tagged-post-4
ParentID: =>SilverStripe\Blog\Model\Blog.FourthBlog
PublishDate: '2012-04-09 15:00:00'
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory

View File

@ -2047,7 +2047,7 @@ cacache@^16.0.0, cacache@^16.1.0, cacache@^16.1.3:
tar "^6.1.11"
unique-filename "^2.0.0"
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
@ -2068,6 +2068,11 @@ camelcase@^5.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30001565:
version "1.0.30001651"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138"
integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==
caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599:
version "1.0.30001603"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001603.tgz#605046a5bdc95ba4a92496d67e062522dce43381"
@ -2496,6 +2501,11 @@ duplexer@^0.1.2:
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
electron-to-chromium@^1.4.601:
version "1.5.12"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.12.tgz#ee31756eaa2e06f2aa606f170b7ad06dd402b4e4"
integrity sha512-tIhPkdlEoCL1Y+PToq3zRNehUaKp3wBX/sr7aclAWdIWjvqAe/Im/H0SiCM4c1Q8BLPHCdoJTol+ZblflydehA==
electron-to-chromium@^1.4.668:
version "1.4.722"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz#99ae3484c5fc0f387d39ad98d77e1f259b9f4074"
@ -3130,7 +3140,7 @@ get-caller-file@^2.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
@ -4609,6 +4619,11 @@ object-inspect@^1.13.1:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
object-inspect@^1.9.0:
version "1.13.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"