From 32efdffbe7530c8edc378396c8a0ad994eac98fa Mon Sep 17 00:00:00 2001 From: KitsuneSolar Date: Sun, 16 Jul 2023 23:53:42 +0000 Subject: [PATCH] 2023-07-16 23:53:42 --- .gitignore | 1 + CHANGELOG.md | 0 CONTRIBUTING.md | 13 ++ LICENSE | 2 +- MW_EXT_SEO.class.php | 372 +++++++++++++++++++++++++++++++++++++++++++ MW_EXT_SEO.php | 16 ++ README.md | 19 ++- composer.json | 46 ++++++ extension.json | 95 +++++++++++ i18n/ru.json | 8 + 10 files changed, 570 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 MW_EXT_SEO.class.php create mode 100644 MW_EXT_SEO.php create mode 100644 composer.json create mode 100644 extension.json create mode 100644 i18n/ru.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..969906b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +- Feedback, wishes and suggestions can be sent by email. +- Constructive criticism, bug descriptions and other reports are welcome. +- Email: mail@ihub.to + +## Sources + +- [**GitHub**](https://github.com/pkgstore) +- [GitLab](https://gitlab.com/pkgstore) (MIRROR) +- [Codeberg](https://codeberg.org/pkgstore) (MIRROR) +- [MosHub](https://hub.mos.ru/pkgstore) (MIRROR) +- [Git.Org.Ru](https://git.org.ru/pkgstore) (MIRROR) diff --git a/LICENSE b/LICENSE index 8dbae8f..62a7665 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Package Store +Copyright (c) 2023 iHub TO Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MW_EXT_SEO.class.php b/MW_EXT_SEO.class.php new file mode 100644 index 0000000..898f32d --- /dev/null +++ b/MW_EXT_SEO.class.php @@ -0,0 +1,372 @@ +getEarliestRevTime()); + $getDateModified = DateTime::createFromFormat('YmdHis', MW_EXT_Kernel::getTitle()->getTouched()); + $getFirstRevision = MW_EXT_Kernel::getTitle()->getFirstRevision(); + $getImage = key($out->getFileSearchOptions()); + $getImageObject = wfFindFile($getImage); + $getRevision = MW_EXT_Kernel::getWikiPage()->getRevision(); + $getHeadline = MW_EXT_Kernel::outClear(MW_EXT_Kernel::getTitle()->getText()); + $getAltHeadline = $getHeadline; + $getKeywords = MW_EXT_Kernel::outClear(str_replace('Категория:', '', implode(', ', array_keys(MW_EXT_Kernel::getTitle()->getParentCategories())))); + $getWordCount = MW_EXT_Kernel::getTitle()->getLength(); + $getArticleURL = $getSiteURL . '/' . '?curid=' . MW_EXT_Kernel::getTitle()->getArticleID(); + $getArticleID = $getSiteURL . '/' . '?curid=' . MW_EXT_Kernel::getTitle()->getArticleID(); + $getExtDescription = $parserOutput->getPageProperty('description'); // Set by "Description2" extension. + + if ($getExtDescription !== false) { + $getDescription = $getExtDescription; + } else { + $getDescription = ''; + }; + + // Get article text. + if ($getRevision) { + $getContent = $getRevision->getContent(Revision::FOR_PUBLIC); + $getContentText = Content::getText($getContent); + $getArticleText = trim(preg_replace('/\s+/', ' ', strip_tags($getContentText))); + } else { + $getArticleText = ''; + } + + $getArticleBody = MW_EXT_Kernel::outClear($getArticleText); + + // Get article created date. + $getDateCreated = $getDateCreated ? $getDateCreated->format('c') : '0'; + + // Get article modified date. + $getDateModified = $getDateModified ? $getDateModified->format('c') : '0'; + + // Get article author. + if ($getFirstRevision) { + $getUser = User::newFromId($getFirstRevision->getUser()); + $getUserName = $getFirstRevision->getUserText(); + $getUserGroups = $getUser->getGroups(); + $getAuthorName = MW_EXT_Kernel::outClear($getUserName); + $getAuthorURL = $getUser->getUserPage()->getFullURL(); + $getAuthorJobTitle = MW_EXT_Kernel::outClear(implode(', ', array_values($getUserGroups))); + } else { + $getAuthorName = MW_EXT_Kernel::getConfig('EXT_SEO_AuthorName'); + $getAuthorJobTitle = ''; + $getAuthorURL = ''; + } + + // Get article image. + if ($getImage && $getImageObject) { + $getImageURL = $getImageObject->getFullURL(); + $getImageWidth = $getImageObject->getWidth(); + $getImageHeight = $getImageObject->getHeight(); + } else { + $getImageURL = $getSiteURL . $getSiteLogo; + $getImageWidth = getimagesize($getImageURL)[0]; + $getImageHeight = getimagesize($getImageURL)[1]; + } + + // ----------------------------------------------------------------------------------------------------------------- + // Init JSON-LD. + // ----------------------------------------------------------------------------------------------------------------- + + $json = []; + + // Loading JSON-LD. + $json['@context'] = 'http://schema.org'; + $json['@type'] = 'Article'; + $json['headline'] = $getHeadline; + $json['alternativeHeadline'] = $getAltHeadline; + $json['description'] = $getDescription; + $json['keywords'] = $getKeywords; + $json['dateCreated'] = $getDateCreated; + $json['datePublished'] = $getDateCreated; + $json['dateModified'] = $getDateModified; + $json['wordCount'] = $getWordCount; + $json['url'] = $getArticleURL; + // $json['articleBody'] = $getArticleBody; + + $json['mainEntityOfPage'] = [ + '@type' => 'WebPage', + '@id' => $getArticleID, + ]; + + $json['author'] = [ + '@type' => 'Person', + 'name' => $getSiteName, + //'jobTitle' => $getAuthorJobTitle, + 'url' => $getSiteURL, + ]; + + $json['image'] = [ + '@type' => 'ImageObject', + 'url' => $getImageURL, + 'height' => $getImageWidth, + 'width' => $getImageHeight, + ]; + + $json['publisher'] = [ + '@type' => 'Organization', + 'name' => $getSiteName, + 'url' => $getSiteURL, + 'logo' => [ + '@type' => 'ImageObject', + 'url' => $getPublisherLogo, + 'height' => 60, + 'width' => 600, + ], + 'address' => [ + '@type' => 'PostalAddress', + 'streetAddress' => $getStreetAddress, + 'addressLocality' => $getAddressLocality, + 'addressRegion' => $getAddressRegion, + 'postalCode' => $getPostalCode, + 'addressCountry' => $getAddressCountry, + ], + 'contactPoint' => [ + '@type' => 'ContactPoint', + 'contactType' => $getContactType, + 'telephone' => $getSitePhone, + 'email' => $getSiteEmail, + 'url' => $getSiteURL, + ], + 'sameAs' => [ + $getURL_Vk, + $getURL_Facebook, + $getURL_Twitter, + $getURL_Discord, + ] + ]; + + // Render JSON-LD. + $json_encode = json_encode($json, JSON_UNESCAPED_UNICODE); + $out->addHeadItem('mw-seo-json', ''); + + // ----------------------------------------------------------------------------------------------------------------- + // HTTP-EQUIV. + // ----------------------------------------------------------------------------------------------------------------- + + // Render HTTP-EQUIV. + $out->addHeadItem('mw-seo-http', Html::element('meta', [ + 'http-equiv' => 'X-UA-Compatible', + 'content' => 'IE=edge', + ])); + + // ----------------------------------------------------------------------------------------------------------------- + // DNS prefetch. + // ----------------------------------------------------------------------------------------------------------------- + + $dns = [ + '//cdn.jsdelivr.net', + '//fonts.googleapis.com', + '//use.fontawesome.com', + ]; + + // Render dns. + foreach ($dns as $key => $value) { + if ($value) { + //$out->addHeadItem( 'mw-seo-dns-' . $key, '' ); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + // Favicon. + // ----------------------------------------------------------------------------------------------------------------- + + $out->addHeadItem('mw-seo-favicon', ''); + + // ----------------------------------------------------------------------------------------------------------------- + // Meta. + // ----------------------------------------------------------------------------------------------------------------- + + $meta = []; + + // Loading Meta. + $meta['viewport'] = 'width=device-width, initial-scale=1, maximum-scale=1'; + $meta['keywords'] = $getKeywords; + $meta['author'] = $getSiteName; + $meta['designer'] = $getSiteName; + $meta['publisher'] = $getSiteName; + $meta['distribution'] = 'web'; + $meta['rating'] = 'general'; + $meta['reply-to'] = $getSiteEmail; + $meta['copyright'] = $getSiteName; + $meta['referrer'] = 'strict-origin'; + $meta['theme-color'] = $getThemeColor; + $meta['msapplication-TileColor'] = $getMSTileColor; + + // Render META. + foreach ($meta as $name => $value) { + if ($value) { + $out->addHeadItem('mw-seo-meta' . $name, Html::element('meta', [ + 'name' => $name, + 'content' => $value, + ])); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + // Link. + // ----------------------------------------------------------------------------------------------------------------- + + $link = []; + + $link['publisher'] = $getPublisher; + $link['manifest'] = $getManifest; + + // Render link. + foreach ($link as $rel => $value) { + if ($value) { + $out->addHeadItem('mw-seo-rel' . $rel, Html::element('link', [ + 'rel' => $rel, + 'href' => $value, + ])); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + // Open Graph. + // ----------------------------------------------------------------------------------------------------------------- + + // OG type. + $getType = MW_EXT_Kernel::getTitle()->isMainPage() ? 'website' : 'article'; + + $og = []; + + // Loading Open Graph. + $og['og:type'] = $getType; + $og['og:site_name'] = $getSiteName; + $og['og:title'] = $getHeadline; + $og['og:description'] = $getDescription; + $og['og:image'] = $getImageURL; + //$og['og:image:width'] = $getImageWidth; + //$og['og:image:height'] = $getImageHeight; + $og['og:url'] = $getArticleURL; + $og['article:published_time'] = $getDateCreated; + $og['article:modified_time'] = $getDateModified; + $og['article:author'] = $getSiteName; + $og['article:publisher'] = $getArticlePublisher; + $og['article:tag'] = $getKeywords; + $og['fb:app_id'] = ''; + + // Render Open Graph. + foreach ($og as $property => $value) { + if ($value) { + $out->addHeadItem('mw-seo-og' . $property, Html::element('meta', [ + 'property' => $property, + 'content' => $value, + ])); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + // Twitter. + // ----------------------------------------------------------------------------------------------------------------- + + $twitter = []; + + // Loading Twitter Card. + $twitter['twitter:card'] = 'summary'; + $twitter['twitter:title'] = $getHeadline; + $twitter['twitter:description'] = $getDescription; + $twitter['twitter:image'] = $getImageURL; + $twitter['twitter:site'] = '@' . $getTwitter_Site; + $twitter['twitter:creator'] = '@' . $getTwitter_Creator; + + // Render Twitter Card. + foreach ($twitter as $name => $value) { + if ($value) { + $out->addHeadItem('mw-seo-twitter' . $name, Html::element('meta', [ + 'name' => $name, + 'content' => $value, + ])); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + // DC. + // ----------------------------------------------------------------------------------------------------------------- + + $dc = []; + + $dc['DC.Title'] = $getHeadline; + $dc['DC.Date.Issued'] = $getDateCreated; + $dc['DC.Date.Created'] = $getDateCreated; + + // Render DC. + foreach ($dc as $name => $value) { + if ($value) { + $out->addHeadItem('mw-seo-dc' . $name, '' . Html::element('meta', [ + 'name' => $name, + 'content' => $value, + ])); + } + } + } + + /** + * Load function. + * + * @param OutputPage $out + * @param ParserOutput $parserOutput + * + * @return void|null + * @throws ConfigException + * @throws MWException + */ + public static function onOutputPageParserOutput(OutputPage $out, ParserOutput $parserOutput) + { + if (!MW_EXT_Kernel::getTitle() || !MW_EXT_Kernel::getTitle()->isContentPage() || !MW_EXT_Kernel::getWikiPage()) { + return null; + } + + self::onRenderSEO($out, $parserOutput); + } +} diff --git a/MW_EXT_SEO.php b/MW_EXT_SEO.php new file mode 100644 index 0000000..1f63682 --- /dev/null +++ b/MW_EXT_SEO.php @@ -0,0 +1,16 @@ +=8.2", + "pkgstore/mediawiki-ext-kernel": "*" + }, + "autoload": { + "classmap": [ + "MW_EXT_SEO.class.php" + ] + }, + "config": { + "optimize-autoloader": true, + "prepend-autoloader": false + }, + "extra": { + "installer-name": "MW_EXT_SEO" + } +} diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..780614a --- /dev/null +++ b/extension.json @@ -0,0 +1,95 @@ +{ + "name": "MW_EXT_SEO", + "version": "1.0.0", + "author": [ + "[https://ihub.to/ iHub TO]", + "[https://kitsune.solar/ Kitsune Solar]", + "..." + ], + "url": "https://ihub.to/", + "descriptionmsg": "mw-seo-desc", + "license-name": "[https://choosealicense.com/licenses/mit/ MIT]", + "type": "", + "require": { + "MediaWiki": ">= 1.31.0", + "extensions": { + "MW_EXT_Kernel": "*" + } + }, + "config": { + "EXT_SEO_Phone": { + "value": "" + }, + "EXT_SEO_Publisher": { + "value": "" + }, + "EXT_SEO_PublisherLogo": { + "value": "" + }, + "EXT_SEO_Manifest": { + "value": "" + }, + "EXT_SEO_URL_Vk": { + "value": "" + }, + "EXT_SEO_URL_Facebook": { + "value": "" + }, + "EXT_SEO_URL_Twitter": { + "value": "" + }, + "EXT_SEO_URL_Discord": { + "value": "" + }, + "EXT_SEO_ThemeColor": { + "value": "#f3f6f9" + }, + "EXT_SEO_MSTileColor": { + "value": "#f3f6f9" + }, + "EXT_SEO_Twitter_Site": { + "value": "metadata_team" + }, + "EXT_SEO_Twitter_Creator": { + "value": "metadata_team" + }, + "EXT_SEO_StreetAddress": { + "value": "" + }, + "EXT_SEO_AddressLocality": { + "value": "Moscow" + }, + "EXT_SEO_AddressRegion": { + "value": "" + }, + "EXT_SEO_PostalCode": { + "value": "" + }, + "EXT_SEO_AddressCountry": { + "value": "RUS" + }, + "EXT_SEO_ContactType": { + "value": "customer service" + }, + "EXT_SEO_AuthorName": { + "value": "SYSTEM" + }, + "EXT_SEO_ArticlePublisher": { + "value": "" + } + }, + "AutoloadClasses": { + "MediaWiki\\Extension\\PkgStore\\MW_EXT_SEO": "MW_EXT_SEO.class.php" + }, + "Hooks": { + "OutputPageParserOutput": [ + "MediaWiki\\Extension\\PkgStore\\MW_EXT_SEO::onOutputPageParserOutput" + ] + }, + "MessagesDirs": { + "MW_EXT_SEO": [ + "i18n" + ] + }, + "manifest_version": 2 +} diff --git a/i18n/ru.json b/i18n/ru.json new file mode 100644 index 0000000..05e9fce --- /dev/null +++ b/i18n/ru.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "iHub TO" + ] + }, + "mw-seo-desc": "Интеграция дополнительного SEO в движок MediaWiki." +}