diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..bf02210
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,11 @@
+name: CI
+
+on:
+ push:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ ci:
+ name: CI
+ uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
diff --git a/.gitignore b/.gitignore
index a4d83b1..52bee4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
/.php_cs.cache
+/public
+/vendor
+composer.lock
+.phpunit.result.cache
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 8984142..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-language: php
-
-matrix:
- include:
- - php: 5.6
- env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
- - php: 7.0
- env: DB=PGSQL PHPUNIT_TEST=1
- - php: 7.1
- env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
-
-before_script:
- - phpenv rehash
- - phpenv config-rm xdebug.ini
-
- - composer validate
- - composer require --prefer-dist --no-update silverstripe/recipe-cms:^1.1
- - if [[ $DB == PGSQL ]]; then composer require --prefer-dist --no-update silverstripe/postgresql:^2.0; fi
- - composer update
-
-script:
- - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
- - if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs -s --standard=PSR2 --exclude=PSR1.Methods.CamelCapsMethodName,Generic.Files.LineLength src/ tests/ *.php; fi
-
-after_success:
- - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
\ No newline at end of file
diff --git a/README.md b/README.md
index f9ef04d..36dbba1 100644
--- a/README.md
+++ b/README.md
@@ -138,6 +138,14 @@ QuinnInteractive\Seo\Analysis\MetaDescAnalysis:
meta_desc_too_long_threshold: 320
```
+### Other Options
+
+Other options can be found in the `private static` variables in the following files. They can be overridden in YAML in the usual way.
+
+- PageHealthExtension.php
+- PageSeoExtension.php
+- SiteConfigSettingsExtension.php
+
## Assumptions
This module assumes that you make use of the default `Content` field provided by `\Page`. If a specific page does not then you can specify one or multiple fields that contain your content.
@@ -182,4 +190,4 @@ public function updateCollateContentFields($content) {
## Version
-1.1.4
+2.0.0
diff --git a/composer.json b/composer.json
index 6ce273e..e085e00 100644
--- a/composer.json
+++ b/composer.json
@@ -1,51 +1,66 @@
{
- "name": "quinninteractive/silverstripe-seo",
- "description": "An all-in-one SEO module for SilverStripe",
- "type": "silverstripe-vendormodule",
- "keywords": ["silverstripe", "seo", "facebook", "twitter", "opengraph", "search", "optimization", "optimisation"],
- "license": "BSD-3-Clause",
- "authors": [
- {
- "name": "Reece Alexander",
- "homepage": "https://vulcandigital.co.nz",
- "role": "author"
+ "name": "quinninteractive/silverstripe-seo",
+ "description": "An all-in-one SEO module for SilverStripe",
+ "type": "silverstripe-vendormodule",
+ "keywords": [
+ "silverstripe",
+ "seo",
+ "facebook",
+ "twitter",
+ "opengraph",
+ "search",
+ "optimization",
+ "optimisation"
+ ],
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Reece Alexander",
+ "homepage": "https://vulcandigital.co.nz",
+ "role": "author"
+ },
+ {
+ "name": "Fred Condo",
+ "homepage": "https://quinn.com",
+ "role": "maintainer"
+ },
+ {
+ "name": "Phil Quinn",
+ "homepage": "https://quinn.com",
+ "role": "maintainer"
+ }
+ ],
+ "require": {
+ "php": "^8.1",
+ "jonom/silverstripe-text-target-length": "^2.0",
+ "kub-at/php-simple-html-dom-parser": "^1.9",
+ "silverstripe/cms": "^5",
+ "wilr/silverstripe-googlesitemaps": "^3.1"
},
- {
- "name": "Fred Condo",
- "homepage": "https://quinn.com",
- "role": "maintainer"
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^1.2"
},
- {
- "name": "Phil Quinn",
- "homepage": "https://quinn.com",
- "role": "maintainer"
- }
- ],
- "require": {
- "silverstripe/cms": "^4.1",
- "jonom/silverstripe-text-target-length": "^2",
- "wilr/silverstripe-googlesitemaps": "^2.1",
- "kub-at/php-simple-html-dom-parser": "^1.7",
- "axllent/silverstripe-trailing-slash": "^2.1"
- },
- "require-dev": {
- "phpunit/phpunit": "^5.7",
- "squizlabs/php_codesniffer": "3.*"
- },
- "autoload": {
- "psr-4": {
- "QuinnInteractive\\Seo\\": "src/",
- "QuinnInteractive\\Seo\\Tests\\": "tests/"
- }
- },
- "extra": {
- "branch-alias": {
- "dev-develop": "2.x-dev"
+ "autoload": {
+ "psr-4": {
+ "QuinnInteractive\\Seo\\": "src/",
+ "QuinnInteractive\\Seo\\Tests\\": "tests/"
+ }
},
- "expose": [
- "dist"
- ]
- },
- "minimum-stability": "dev",
- "prefer-stable": true
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "3.x-dev"
+ },
+ "expose": [
+ "dist"
+ ]
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "allow-plugins": {
+ "composer/installers": true,
+ "silverstripe/vendor-plugin": true
+ }
+ }
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 85eb950..8190532 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,6 +1,6 @@
-
+
- tests
+ ./tests
@@ -11,4 +11,4 @@
-
\ No newline at end of file
+
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000..cb14d8a
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,14 @@
+withPaths([
+ __DIR__ . '/src',
+ __DIR__ . '/tests',
+ ])
+ ->withPhpSets(php81: true)
+ ->withImportNames(importShortClasses: false)
+ ->withTypeCoverageLevel(0);
diff --git a/src/Analysis/Analysis.php b/src/Analysis/Analysis.php
index e4c5df8..c34f30d 100644
--- a/src/Analysis/Analysis.php
+++ b/src/Analysis/Analysis.php
@@ -72,7 +72,7 @@ public function getContent()
$parser = $this->getRenderedHtmlDomParser();
$output = [];
foreach ($parser->find('p,h1,h2,h3,h4,h5') as $item) {
- $output[] = strip_tags(html_entity_decode($item->innertext()));
+ $output[] = strip_tags(html_entity_decode((string) $item->innertext()));
}
$output = array_filter($output);
diff --git a/src/Analysis/FocusKeywordContentAnalysis.php b/src/Analysis/FocusKeywordContentAnalysis.php
index a5d8473..6016572 100644
--- a/src/Analysis/FocusKeywordContentAnalysis.php
+++ b/src/Analysis/FocusKeywordContentAnalysis.php
@@ -8,9 +8,9 @@
*/
class FocusKeywordContentAnalysis extends Analysis
{
- const FOCUS_KEYWORD_NOT_FOUND = 0;
- const FOCUS_KEYWORD_SUCCESS = 1;
- const FOCUS_KEYWORD_UNSET = -1;
+ public const FOCUS_KEYWORD_NOT_FOUND = 0;
+ public const FOCUS_KEYWORD_SUCCESS = 1;
+ public const FOCUS_KEYWORD_UNSET = -1;
private static $hidden_levels = [
'default'
diff --git a/src/Analysis/FocusKeywordUniqueAnalysis.php b/src/Analysis/FocusKeywordUniqueAnalysis.php
index da2a801..1ce3aa8 100644
--- a/src/Analysis/FocusKeywordUniqueAnalysis.php
+++ b/src/Analysis/FocusKeywordUniqueAnalysis.php
@@ -8,9 +8,9 @@
*/
class FocusKeywordUniqueAnalysis extends Analysis
{
- const FOCUS_KEYWORD_INUSE = 0;
- const FOCUS_KEYWORD_SUCCESS = 1;
- const FOCUS_KEYWORD_UNSET = -1;
+ public const FOCUS_KEYWORD_INUSE = 0;
+ public const FOCUS_KEYWORD_SUCCESS = 1;
+ public const FOCUS_KEYWORD_UNSET = -1;
/**
* @return string
diff --git a/src/Analysis/FocusKeywordUrlAnalysis.php b/src/Analysis/FocusKeywordUrlAnalysis.php
index d725cf8..ea71246 100644
--- a/src/Analysis/FocusKeywordUrlAnalysis.php
+++ b/src/Analysis/FocusKeywordUrlAnalysis.php
@@ -10,10 +10,10 @@
*/
class FocusKeywordUrlAnalysis extends Analysis
{
- const FOCUS_KEYWORD_IRRELEVANT = -2;
- const FOCUS_KEYWORD_NOT_IN_URL = 0;
- const FOCUS_KEYWORD_SUCCESS = 1;
- const FOCUS_KEYWORD_UNSET = -1;
+ public const FOCUS_KEYWORD_IRRELEVANT = -2;
+ public const FOCUS_KEYWORD_NOT_IN_URL = 0;
+ public const FOCUS_KEYWORD_SUCCESS = 1;
+ public const FOCUS_KEYWORD_UNSET = -1;
private static $hidden_levels = [
'default'
@@ -38,19 +38,19 @@ public function responses()
{
return [
static::FOCUS_KEYWORD_IRRELEVANT => [
- _t( __CLASS__ . '.FOCUS_KEYWORD_IRRELEVANT', 'The focus keyword is irrelevant on the home page; this message will not display'),
+ _t( self::class . '.FOCUS_KEYWORD_IRRELEVANT', 'The focus keyword is irrelevant on the home page; this message will not display'),
'default'
],
static::FOCUS_KEYWORD_UNSET => [
- _t( __CLASS__ . '.FOCUS_KEYWORD_UNSET', 'The focus keyword has not been set; consider setting this to improve content analysis'),
+ _t( self::class . '.FOCUS_KEYWORD_UNSET', 'The focus keyword has not been set; consider setting this to improve content analysis'),
'default'
],
static::FOCUS_KEYWORD_NOT_IN_URL => [
- _t( __CLASS__ . '.FOCUS_KEYWORD_NOT_IN_URL', 'The focus keyword is not in the url segment; consider changing this and if you do SilverStripe will automatically redirect your old URL!'),
+ _t( self::class . '.FOCUS_KEYWORD_NOT_IN_URL', 'The focus keyword is not in the url segment; consider changing this and if you do SilverStripe will automatically redirect your old URL!'),
'warning'
],
static::FOCUS_KEYWORD_SUCCESS => [
- _t( __CLASS__ . '.FOCUS_KEYWORD_SUCCESS', 'The focus keyword is in the url segment; this is great!'),
+ _t( self::class . '.FOCUS_KEYWORD_SUCCESS', 'The focus keyword is in the url segment; this is great!'),
'success'
],
];
diff --git a/src/Analysis/TitleAnalysis.php b/src/Analysis/TitleAnalysis.php
index a10232f..8b8b557 100644
--- a/src/Analysis/TitleAnalysis.php
+++ b/src/Analysis/TitleAnalysis.php
@@ -8,13 +8,13 @@
*/
class TitleAnalysis extends Analysis
{
- const TITLE_FOCUS_KEYWORD_POSITION = 4;
- const TITLE_IS_HOME = -1;
- const TITLE_NO_FOCUS_KEYWORD = 3; // only checked if the focus keyword has been defined
- const TITLE_OK_BUT_SHORT = 1;
- const TITLE_SUCCESS = 5;
- const TITLE_TOO_LONG = 2;
- const TITLE_TOO_SHORT = 0;
+ public const TITLE_FOCUS_KEYWORD_POSITION = 4;
+ public const TITLE_IS_HOME = -1;
+ public const TITLE_NO_FOCUS_KEYWORD = 3; // only checked if the focus keyword has been defined
+ public const TITLE_OK_BUT_SHORT = 1;
+ public const TITLE_SUCCESS = 5;
+ public const TITLE_TOO_LONG = 2;
+ public const TITLE_TOO_SHORT = 0;
/**
* @return array
diff --git a/src/Analysis/WordCountAnalysis.php b/src/Analysis/WordCountAnalysis.php
index def188e..4775358 100644
--- a/src/Analysis/WordCountAnalysis.php
+++ b/src/Analysis/WordCountAnalysis.php
@@ -10,8 +10,8 @@
*/
class WordCountAnalysis extends Analysis
{
- const WORD_COUNT_ABOVE_MIN = 1;
- const WORD_COUNT_BELOW_MIN = 0;
+ public const WORD_COUNT_ABOVE_MIN = 1;
+ public const WORD_COUNT_BELOW_MIN = 0;
/**
* @return int
diff --git a/src/Builders/FacebookMetaGenerator.php b/src/Builders/FacebookMetaGenerator.php
index 7638c25..5886495 100644
--- a/src/Builders/FacebookMetaGenerator.php
+++ b/src/Builders/FacebookMetaGenerator.php
@@ -105,11 +105,11 @@ public function process()
$tags = [];
if ($this->getTitle()) {
- $tags[] = sprintf('', htmlentities($this->getTitle()));
+ $tags[] = sprintf('', htmlentities((string) $this->getTitle()));
}
if ($this->getDescription()) {
- $tags[] = sprintf('', htmlentities($this->getDescription()));
+ $tags[] = sprintf('', htmlentities((string) $this->getDescription()));
}
if ($this->getType()) {
@@ -136,11 +136,9 @@ public function process()
}
/**
- * @param mixed $description
- *
* @return FacebookMetaGenerator
*/
- public function setDescription($description)
+ public function setDescription(mixed $description)
{
$this->description = $description;
return $this;
@@ -172,13 +170,11 @@ public function setImageHeight($height)
}
/**
- * @param mixed $imageUrl
- *
* @return FacebookMetaGenerator
*/
- public function setImageUrl($imageUrl)
+ public function setImageUrl(mixed $imageUrl)
{
- if ($imageUrl && (substr($imageUrl, 0, 1) === '/' || substr($imageUrl, 0, 4) !== 'http')) {
+ if ($imageUrl && (str_starts_with((string) $imageUrl, '/') || !str_starts_with((string) $imageUrl, 'http'))) {
throw new \InvalidArgumentException(
'A relative or invalid URL was detected; you must provide the full absolute URL'
);
@@ -200,23 +196,20 @@ public function setImageWidth($width)
}
/**
- * @param mixed $title
- *
* @return FacebookMetaGenerator
*/
- public function setTitle($title)
+ public function setTitle(mixed $title)
{
$this->title = $title;
return $this;
}
/**
- * @param mixed $type
*
* @return FacebookMetaGenerator
* @throws \Exception
*/
- public function setType($type)
+ public function setType(mixed $type)
{
if (!in_array($type, array_keys(static::getValidTypes()))) {
throw new \Exception(sprintf(
@@ -231,13 +224,11 @@ public function setType($type)
}
/**
- * @param mixed $url
- *
* @return FacebookMetaGenerator
*/
- public function setUrl($url)
+ public function setUrl(mixed $url)
{
- if ($url && (substr($url, 0, 1) === '/' || substr($url, 0, 4) !== 'http')) {
+ if ($url && (str_starts_with((string) $url, '/') || !str_starts_with((string) $url, 'http'))) {
throw new \InvalidArgumentException(
'A relative URL was detected; you must provide the full absolute URL instead'
);
diff --git a/src/Builders/TwitterMetaGenerator.php b/src/Builders/TwitterMetaGenerator.php
index fcaa503..ee07a8e 100644
--- a/src/Builders/TwitterMetaGenerator.php
+++ b/src/Builders/TwitterMetaGenerator.php
@@ -95,11 +95,11 @@ public function process()
$tags[] = '';
if ($this->getTitle()) {
- $tags[] = sprintf('', htmlentities($this->getTitle()));
+ $tags[] = sprintf('', htmlentities((string) $this->getTitle()));
}
if ($this->getDescription()) {
- $tags[] = sprintf('', htmlentities($this->getDescription()));
+ $tags[] = sprintf('', htmlentities((string) $this->getDescription()));
}
if ($this->getImageUrl()) {
@@ -129,11 +129,9 @@ public function setCreator($creator)
}
/**
- * @param mixed $description
- *
* @return TwitterMetaGenerator
*/
- public function setDescription($description)
+ public function setDescription(mixed $description)
{
$this->description = $description;
@@ -141,13 +139,11 @@ public function setDescription($description)
}
/**
- * @param mixed $imageUrl
- *
* @return TwitterMetaGenerator
*/
- public function setImageUrl($imageUrl)
+ public function setImageUrl(mixed $imageUrl)
{
- if ($imageUrl && (substr($imageUrl, 0, 1) === '/' || substr($imageUrl, 0, 4) !== 'http')) {
+ if ($imageUrl && (str_starts_with((string) $imageUrl, '/') || !str_starts_with((string) $imageUrl, 'http'))) {
throw new \InvalidArgumentException(
'A relative or invalid URL was detected, your must provide the full absolute URL'
);
@@ -158,11 +154,9 @@ public function setImageUrl($imageUrl)
}
/**
- * @param mixed $title
- *
* @return TwitterMetaGenerator
*/
- public function setTitle($title)
+ public function setTitle(mixed $title)
{
$this->title = $title;
diff --git a/src/Extensions/MemberExtension.php b/src/Extensions/MemberExtension.php
index 388c7ed..26b7230 100644
--- a/src/Extensions/MemberExtension.php
+++ b/src/Extensions/MemberExtension.php
@@ -27,6 +27,8 @@ public function updateCMSFields(FieldList $fields)
$fields->addFieldsToTab('Root.Main', [
TextField::create('TwitterAccountName')
], 'Password');
+ } else {
+ $fields->removeByName('TwitterAccountName');
}
}
}
diff --git a/src/Extensions/PageHealthExtension.php b/src/Extensions/PageHealthExtension.php
index 7a37984..b22f487 100644
--- a/src/Extensions/PageHealthExtension.php
+++ b/src/Extensions/PageHealthExtension.php
@@ -2,6 +2,7 @@
namespace QuinnInteractive\Seo\Extensions;
+use SilverStripe\ErrorPage\ErrorPage;
use KubAT\PhpSimple\HtmlDomParser;
use QuinnInteractive\Seo\Forms\GoogleSearchPreview;
use QuinnInteractive\Seo\Forms\HealthAnalysisField;
@@ -22,7 +23,13 @@
*/
class PageHealthExtension extends DataExtension
{
- const EMPTY_HTML = '
';
+ public const EMPTY_HTML = '';
+
+ private static $tab_name = 'Root.SEO';
+
+ private static bool $move_default_meta_fields = true;
+
+ private static bool $start_closed = true;
/**
* @var string|null
@@ -103,14 +110,14 @@ public function updateCMSFields(FieldList $fields)
return;
}
- if ($this->owner instanceof \SilverStripe\ErrorPage\ErrorPage) {
+ if (class_exists('\SilverStripe\ErrorPage\ErrorPage') && $this->owner instanceof ErrorPage) {
return;
}
$dom = $this->getRenderedHtmlDomParser();
if ($dom) {
- $fields->addFieldsToTab('Root.Main', [
+ $fields->addFieldsToTab($this->owner->config()->get('tab_name'), [
ToggleCompositeField::create('SEOHealthAnalysis', 'SEO Health Analysis', [
GoogleSearchPreview::create(
'GoogleSearchPreview',
@@ -120,8 +127,17 @@ public function updateCMSFields(FieldList $fields)
),
TextField::create('FocusKeyword', 'Set focus keyword'),
HealthAnalysisField::create('ContentAnalysis', 'Content Analysis', $this->getOwner()),
- ])
+ ])->setStartClosed($this->owner->config()->get('start_closed'))
], 'Metadata');
+
+ if ($this->owner->config()->get('move_default_meta_fields')) {
+ $meta = $fields->fieldByName('Root.Main.Metadata');
+
+ if ($meta) {
+ $fields->removeByName('Metadata');
+ $fields->addFieldToTab($this->owner->config()->get('tab_name'), $meta);
+ }
+ }
}
}
}
diff --git a/src/Extensions/PageSeoExtension.php b/src/Extensions/PageSeoExtension.php
index 556c460..1099f18 100644
--- a/src/Extensions/PageSeoExtension.php
+++ b/src/Extensions/PageSeoExtension.php
@@ -35,6 +35,12 @@ class PageSeoExtension extends DataExtension
{
use Configurable;
+ private static string $tab_name = 'Root.SEO';
+
+ private static bool $start_closed = true;
+
+ private static bool $use_composite_field = true;
+
private static $cascade_deletes = [
'FacebookPageImage',
'TwitterPageImage'
@@ -74,7 +80,7 @@ class PageSeoExtension extends DataExtension
*/
public function MetaTags(&$tags)
{
- $tags = explode(PHP_EOL, $tags);
+ $tags = explode(PHP_EOL, (string) $tags);
$tags = array_merge(
$tags,
Seo::getCanonicalUrlLink($this->getOwner()),
@@ -103,51 +109,68 @@ public function onBeforeWrite()
public function updateCMSFields(FieldList $fields)
{
parent::updateCMSFields($fields);
+
$suppressMessaging = false;
+
if (Controller::curr() instanceof HistoryViewerController) { // avoid cluttering the history comparison UI
$suppressMessaging = true;
}
- $fields->addFieldsToTab('Root.Main', [
- ToggleCompositeField::create('FacebookSeoComposite', 'Facebook SEO', [
- DropdownField::create('FacebookPageType', 'Type', FacebookMetaGenerator::getValidTypes()),
- TextField::create('FacebookPageTitle', 'Title')
- ->setAttribute('placeholder', $this->getOwner()->Title)
- ->setRightTitle($suppressMessaging ? '' : 'If blank, inherits default page title')
- ->setTargetLength(45, 25, 70),
- UploadField::create('FacebookPageImage', 'Image')
- ->setRightTitle($suppressMessaging
- ? ''
- : 'Facebook recommends images to be 1200 x 630 pixels. ' .
- 'If no image is provided, Facebook will choose the first image that appears on the page, ' .
- 'which usually has bad results')
- ->setFolderName('seo'),
- TextareaField::create('FacebookPageDescription', 'Description')
- ->setAttribute('placeholder', $this->getOwner()->MetaDescription ?:
- $this->getOwner()->dbObject('Content')->LimitCharacters(297))
- ->setRightTitle($suppressMessaging
- ? ''
- : 'If blank, inherits meta description if it exists ' .
- 'or gets the first 297 characters from content')
- ->setTargetLength(200, 160, 320),
- ]),
- ToggleCompositeField::create('TwitterSeoComposite', 'Twitter SEO', [
- TextField::create('TwitterPageTitle', 'Title')
- ->setAttribute('placeholder', $this->getOwner()->Title)
- ->setRightTitle($suppressMessaging ? '' : 'If blank, inherits default page title')
- ->setTargetLength(45, 25, 70),
- UploadField::create('TwitterPageImage', 'Image')
- ->setRightTitle($suppressMessaging ? '' : 'Must be at least 280x150 pixels')
- ->setFolderName('seo'),
- TextareaField::create('TwitterPageDescription', 'Description')
- ->setAttribute('placeholder', $this->getOwner()->MetaDescription ?:
- $this->getOwner()->dbObject('Content')->LimitCharacters(297))
- ->setRightTitle($suppressMessaging
- ? ''
- : 'If blank, inherits meta description if it exists ' .
+ $openGraphFields = [
+ DropdownField::create('FacebookPageType', 'Type', FacebookMetaGenerator::getValidTypes()),
+ TextField::create('FacebookPageTitle', 'Title')
+ ->setAttribute('placeholder', $this->getOwner()->Title)
+ ->setRightTitle($suppressMessaging ? '' : 'If blank, inherits default page title')
+ ->setTargetLength(45, 25, 70),
+ UploadField::create('FacebookPageImage', 'Image')
+ ->setRightTitle($suppressMessaging
+ ? ''
+ : 'Facebook recommends images to be 1200 x 630 pixels. ' .
+ 'If no image is provided, Facebook will choose the first image that appears on the page, ' .
+ 'which usually has bad results')
+ ->setFolderName('seo'),
+ TextareaField::create('FacebookPageDescription', 'Description')
+ ->setAttribute('placeholder', $this->getOwner()->MetaDescription ?:
+ $this->getOwner()->dbObject('Content')->LimitCharacters(297))
+ ->setRightTitle($suppressMessaging
+ ? ''
+ : 'If blank, inherits meta description if it exists ' .
+ 'or gets the first 297 characters from content')
+ ->setTargetLength(200, 160, 320),
+ ];
+
+
+ $fields->addFieldsToTab(
+ $this->config()->get('tab_name'),
+ $this->config()->get('use_composite_field') ? [
+ ToggleCompositeField::create('FacebookSeoComposite', 'Open Graph', $openGraphFields)
+ ->setStartClosed($this->config()->get('start_closed')),
+ ] : $openGraphFields,
+ 'Metadata'
+ );
+
+ $fields->addFieldsToTab(
+ $this->config()->get('tab_name'),
+ [
+ ToggleCompositeField::create('TwitterSeoComposite', 'Twitter SEO', [
+ TextField::create('TwitterPageTitle', 'Title')
+ ->setAttribute('placeholder', $this->getOwner()->Title)
+ ->setRightTitle($suppressMessaging ? '' : 'If blank, inherits default page title')
+ ->setTargetLength(45, 25, 70),
+ UploadField::create('TwitterPageImage', 'Image')
+ ->setRightTitle($suppressMessaging ? '' : 'Must be at least 280x150 pixels')
+ ->setFolderName('seo'),
+ TextareaField::create('TwitterPageDescription', 'Description')
+ ->setAttribute('placeholder', $this->getOwner()->MetaDescription ?:
+ $this->getOwner()->dbObject('Content')->LimitCharacters(297))
+ ->setRightTitle($suppressMessaging
+ ? ''
+ : 'If blank, inherits meta description if it exists ' .
'or gets the first 297 characters from content')
- ->setTargetLength(200, 160, 320),
- ])
- ], 'Metadata');
+ ->setTargetLength(200, 160, 320),
+ ])
+ ],
+ 'Metadata'
+ );
}
}
diff --git a/src/Extensions/SiteConfigSettingsExtension.php b/src/Extensions/SiteConfigSettingsExtension.php
index 4a3f714..be08cdf 100644
--- a/src/Extensions/SiteConfigSettingsExtension.php
+++ b/src/Extensions/SiteConfigSettingsExtension.php
@@ -24,6 +24,8 @@ class SiteConfigSettingsExtension extends DataExtension
{
use Configurable;
+ private static string $tab_name = 'Root.SEO';
+
private static $casting = [
'GoogleAnalytics' => 'HTMLText'
];
@@ -44,7 +46,7 @@ public function updateCMSFields(FieldList $fields)
$snPixelHelp = 'https://businesshelp.snapchat.com/en-US/article/snap-pixel';
$gaHelp = 'https://support.google.com/analytics/answer/1008080?hl=en';
- $fields->addFieldsToTab('Root.SEO', [
+ $fields->addFieldsToTab($this->config()->get('tab_name'), [
TextField::create('TwitterAccountName'),
TextareaField::create('GoogleAnalytics', 'Google Analytics')->setRightTitle($this->getHelpLink($gaHelp)),
ToggleCompositeField::create(null, 'Pixels', [
diff --git a/src/Forms/GoogleSearchPreview.php b/src/Forms/GoogleSearchPreview.php
index 3648abe..60133be 100644
--- a/src/Forms/GoogleSearchPreview.php
+++ b/src/Forms/GoogleSearchPreview.php
@@ -2,6 +2,7 @@
namespace QuinnInteractive\Seo\Forms;
+use SilverStripe\Forms\FormField;
use QuinnInteractive\Seo\Extensions\PageHealthExtension;
use QuinnInteractive\Seo\Extensions\PageSeoExtension;
use SilverStripe\Control\Controller;
@@ -32,7 +33,7 @@ class GoogleSearchPreview extends LiteralField
* HealthAnalysisField constructor.
*
* @param string $name
- * @param \SilverStripe\Forms\FormField|string $title
+ * @param FormField|string $title
* @param \Page|PageHealthExtension|PageSeoExtension $page
* @param simple_html_dom $domParser
*/
@@ -91,10 +92,10 @@ public function limitDescriptionLength($text) {
public function highlight($haystack, $needle)
{
if (!$needle) {
- return strip_tags($haystack);
+ return strip_tags((string) $haystack);
}
- return preg_replace('/\b(' . $needle . ')\b/i', '$0', strip_tags($haystack));
+ return preg_replace('/\b(' . $needle . ')\b/i', '$0', strip_tags((string) $haystack));
}
/**
diff --git a/src/Forms/HealthAnalysisField.php b/src/Forms/HealthAnalysisField.php
index fc7a858..5925120 100644
--- a/src/Forms/HealthAnalysisField.php
+++ b/src/Forms/HealthAnalysisField.php
@@ -2,6 +2,7 @@
namespace QuinnInteractive\Seo\Forms;
+use SilverStripe\Forms\FormField;
use QuinnInteractive\Seo\Analysis\Analysis;
use QuinnInteractive\Seo\Extensions\PageHealthExtension;
use SilverStripe\CMS\Model\SiteTree;
@@ -35,7 +36,7 @@ class HealthAnalysisField extends LiteralField
* HealthAnalysisField constructor.
*
* @param string $name
- * @param \SilverStripe\Forms\FormField|string $title
+ * @param FormField|string $title
* @param \Page $page
*/
public function __construct($name, $title, SiteTree $page)
diff --git a/src/Seo.php b/src/Seo.php
index 00296a8..bcfb654 100644
--- a/src/Seo.php
+++ b/src/Seo.php
@@ -111,7 +111,7 @@ public static function getFacebookMetaTags($owner)
$generator = FacebookMetaGenerator::create();
$generator->setTitle($owner->FacebookPageTitle ?: $owner->Title);
- $generator->setDescription($owner->FacebookPageDescription ?: $owner->MetaDescription ?: $owner->Content);
+ $generator->setDescription(($owner->FacebookPageDescription ?: $owner->MetaDescription) ?: $owner->Content);
$generator->setImageUrl(($owner->FacebookPageImage()->exists())
? $owner->FacebookPageImage()->AbsoluteLink()
: null);
@@ -144,7 +144,7 @@ public static function getPixels()
$ours = array_keys(SiteConfigSettingsExtension::config()->get('db'));
$db = SiteConfig::config()->get('db');
foreach ($db as $k => $v) {
- if (strstr($k, 'Pixel') && in_array($k, $ours)) {
+ if (strstr((string) $k, 'Pixel') && in_array($k, $ours)) {
if (is_null($siteConfig->{$k})) {
continue;
}
@@ -166,7 +166,7 @@ public static function getTwitterMetaTags($owner)
{
$generator = TwitterMetaGenerator::create();
$generator->setTitle($owner->TwitterPageTitle ?: $owner->Title);
- $generator->setDescription($owner->TwitterPageDescription ?: $owner->MetaDescription ?: $owner->Content);
+ $generator->setDescription(($owner->TwitterPageDescription ?: $owner->MetaDescription) ?: $owner->Content);
$generator->setImageUrl(($owner->TwitterPageImage()->exists())
? $owner->TwitterPageImage()->AbsoluteLink()
: null);
diff --git a/tests/.gitkeep b/tests/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/Extensions/PageSeoExtensionTest.php b/tests/Extensions/PageSeoExtensionTest.php
index d4a5416..b3937be 100644
--- a/tests/Extensions/PageSeoExtensionTest.php
+++ b/tests/Extensions/PageSeoExtensionTest.php
@@ -15,7 +15,7 @@ class PageSeoExtensionTest extends FunctionalTest
/** @var \Page|PageSeoExtension */
protected $page;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
diff --git a/tests/Pages.yml b/tests/Pages.yml
index 44d6900..16a8adb 100644
--- a/tests/Pages.yml
+++ b/tests/Pages.yml
@@ -10,4 +10,4 @@ Page:
URLSegment: industrial-panel-tanks
Content: Something awesome about industrial panel tanks
FocusKeyword: industrial panel tanks
- MetaDescrpition: some long meta description that contains the focus keyword industrial panel tanks
\ No newline at end of file
+ MetaDescrpition: some long meta description that contains the focus keyword industrial panel tanks
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..529755b
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,23 @@
+ 'Varchar'
+ ];
+ }
+}
+
+if (!class_exists('PageController') && class_exists(ContentController::class)) {
+ class PageController extends ContentController
+ {
+ }
+}
+
+require_once dirname(__DIR__) . '/vendor/silverstripe/framework/tests/bootstrap.php';