Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement naive feed configuration storage #1399

Draft
wants to merge 8 commits into
base: 5.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use craft\events\RegisterUrlRulesEvent;
use craft\feedme\base\PluginTrait;
use craft\feedme\models\Settings;
use craft\feedme\services\FeedConfigStorage;
use craft\feedme\services\DataTypes;
use craft\feedme\services\Elements;
use craft\feedme\services\Feeds;
Expand All @@ -26,6 +27,7 @@
/**
* Class Plugin
*
* @property-read FeedConfigStorage $config
* @property-read DataTypes $data
* @property-read Elements $elements
* @property-read Feeds $feeds
Expand All @@ -50,6 +52,7 @@ public static function config(): array
{
return [
'components' => [
'config' => ['class' => FeedConfigStorage::class],
'data' => ['class' => DataTypes::class],
'elements' => ['class' => Elements::class],
'feeds' => ['class' => Feeds::class],
Expand Down
10 changes: 10 additions & 0 deletions src/base/PluginTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Craft;
use craft\feedme\Plugin;
use craft\feedme\services\FeedConfigStorage;
use craft\feedme\services\DataTypes;
use craft\feedme\services\Elements;
use craft\feedme\services\Feeds;
Expand Down Expand Up @@ -78,6 +79,15 @@ public static function debug($message): void
// Public Methods
// =========================================================================

/**
* @return FeedConfigStorage
* @throws \yii\base\InvalidConfigException
*/
public function getFeedConfigStorage(): FeedConfigStorage
{
return $this->get('feedConfigStorage');
}

/**
* @return DataTypes
* @throws \yii\base\InvalidConfigException
Expand Down
66 changes: 66 additions & 0 deletions src/console/controllers/FeedConfigStorageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace craft\feedme\console\controllers;

use craft\feedme\Plugin;
use craft\helpers\Console;
use yii\console\Controller;
use yii\console\ExitCode;

/**
* Read from or write to configuration file
*
* @property Plugin $module
*/
class FeedConfigStorageController extends Controller
{
// Public Methods
// =========================================================================

/**
* @inheritDoc
*/
public function options($actionID): array
{
$options = parent::options($actionID);
return $options;
}

/**
* Writes feeds records to file
*
* @return int
*/
public function actionWrite(): int
{
return Plugin::$plugin->config->write() ? ExitCode::CANTCREAT : ExitCode::OK;
}

/**
* Reads feeds records from file
*
* @return int
*/
public function actionRead(): int
{
$result = Plugin::$plugin->config->read();

if($result->success) {
return ExitCode::OK;
}

$this->stderr("FAILED FEEDS" . PHP_EOL, Console::FG_RED);
foreach($result->failed_feeds as $feed) {
$this->stderr("({$feed->id}) {$feed->name}".PHP_EOL, Console::FG_RED);
}

if(count($result->success_feeds) > 0) {
$this->stdout(PHP_EOL . "SUCCESSFUL FEEDS" . PHP_EOL, Console::FG_GREEN);
foreach ($result->success_feeds as $feed) {
$this->stdout("({$feed->id}) {$feed->name} " . PHP_EOL, Console::FG_GREEN);
}
}

return ExitCode::UNSPECIFIED_ERROR;
}
}
2 changes: 2 additions & 0 deletions src/console/controllers/FeedsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use yii\console\ExitCode;

/**
* Queues up feed imports
*
* @property Plugin $module
*/
class FeedsController extends Controller
Expand Down
36 changes: 31 additions & 5 deletions src/elements/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,15 @@ public function getQuery($settings, array $params = []): mixed
$section = $this->element->getSection();
}

list($sectionId, $entryTypeId) = $this->getSectionAndElementGroupIdsFromUids(
$settings['elementGroup'][EntryElement::class]['section'],
$settings['elementGroup'][EntryElement::class]['entryType']
);

$query = EntryElement::find()
->status(null)
->sectionId($settings['elementGroup'][EntryElement::class]['section'])
->typeId($settings['elementGroup'][EntryElement::class]['entryType']);
->sectionId($sectionId)
->typeId($entryTypeId);

if (isset($section) && $section->propagationMethod === Section::PROPAGATION_METHOD_CUSTOM) {
$query->site('*')
Expand All @@ -119,16 +124,37 @@ public function getQuery($settings, array $params = []): mixed
return $query;
}


public function getSectionAndElementGroupIdsFromUids(string $sectionUid, string $elementGroupUid): array
{
$section = Craft::$app->sections->getSectionByUid($sectionUid);
$entryTypes = $section->getEntryTypes();
$entryTypeId = null;
foreach ($entryTypes as $entryType) {
if ($entryType->uid === $elementGroupUid) {
$entryTypeId = $entryType->id;
break;
}
}
return [$section->id, $entryTypeId];
}

/**
* @inheritDoc
*/
public function setModel($settings): ElementInterface
{
$this->element = new EntryElement();
$this->element->sectionId = $settings['elementGroup'][EntryElement::class]['section'];
$this->element->typeId = $settings['elementGroup'][EntryElement::class]['entryType'];

$section = Craft::$app->getSections()->getSectionById($this->element->sectionId);
list($sectionId, $entryTypeId) = $this->getSectionAndElementGroupIdsFromUids(
$settings['elementGroup'][EntryElement::class]['section'],
$settings['elementGroup'][EntryElement::class]['entryType']
);

$this->element->sectionId = $sectionId;
$this->element->typeId = $entryTypeId;

$section = Craft::$app->getSections()->getSectionByUid($this->element->section->uid);
$siteId = Hash::get($settings, 'siteId');

if ($siteId) {
Expand Down
2 changes: 1 addition & 1 deletion src/fields/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ private function _createElement($dataValue): ?int
$element->typeId = $typeId;

$siteId = Hash::get($this->feed, 'siteId');
$section = Craft::$app->getSections()->getSectionById($element->sectionId);
$section = Craft::$app->getSections()->getSectionByUid($element->sectionUid);

if ($siteId) {
$element->siteId = $siteId;
Expand Down
49 changes: 49 additions & 0 deletions src/models/FeedModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use craft\feedme\base\ElementInterface;
use craft\feedme\helpers\DuplicateHelper;
use craft\feedme\Plugin;
use craft\helpers\Db;
use craft\helpers\Json;
use DateTime;

/**
Expand Down Expand Up @@ -254,4 +256,51 @@ public function rules(): array
[['backup', 'setEmptyValues'], 'boolean'],
];
}

/**
* Retrieves the attributes in a format for a record
*
* @return array An array containing the record attributes
*/
public function getRecordAttributes(): array
{
$attributes = [
"name" => $this->name,
"feedUrl" => $this->feedUrl,
"feedType" => $this->feedType,
"primaryElement" => $this->primaryElement,
"elementType" => $this->elementType,
"siteId" => $this->siteId,
"singleton" => $this->singleton,
"duplicateHandle" => $this->duplicateHandle,
"updateSearchIndexes" => $this->updateSearchIndexes,
"paginationNode" => $this->paginationNode,
"passkey" => $this->passkey,
"backup" => $this->backup,
"setEmptyValues" => $this->setEmptyValues,

// These might be automatically updated, but in the case of writing
// the feed record to a file, we want these values.
"dateCreated" => Db::prepareDateForDb($this->dateCreated),
"dateUpdated" => Db::prepareDateForDb($this->dateUpdated)
];

if ($this->elementGroup) {
$attributes["elementGroup"] = Json::encode($this->elementGroup);
}

if ($this->duplicateHandle) {
$attributes["duplicateHandle"] = Json::encode($this->duplicateHandle);
}

if ($this->fieldMapping) {
$attributes["fieldMapping"] = Json::encode($this->fieldMapping);
}

if ($this->fieldUnique) {
$attributes["fieldUnique"] = Json::encode($this->fieldUnique);
}

return $attributes;
}
}
9 changes: 7 additions & 2 deletions src/services/DataTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,15 @@ public function getRawData($url, $feedId = null): array
*/
public function getFeedData($feedModel, bool $usePrimaryElement = true): mixed
{
$feedDataResponse = $feedModel->getDataType()->getFeed($feedModel->feedUrl, $feedModel, $usePrimaryElement);
// Dynamism in the feedUrl -- use twig to inject dates, for example
$twig = \Craft::$app->getView()->getTwig();
$template = twig_template_from_string($twig, $feedModel->feedUrl);
$feedUrl = $template->render([]);

$feedDataResponse = $feedModel->getDataType()->getFeed($feedUrl, $feedModel, $usePrimaryElement);

$event = new FeedDataEvent([
'url' => $feedModel->feedUrl,
'url' => $feedUrl,
'response' => $feedDataResponse,
'feedId' => $feedModel->id,
]);
Expand Down
78 changes: 78 additions & 0 deletions src/services/FeedConfigStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace craft\feedme\services;

use craft\base\Component;
use craft\feedme\Plugin;
use craft\feedme\records\FeedRecord;
use Symfony\Component\Yaml\Yaml;

/**
*
*
*/
class FeedConfigStorage extends Component
{
/**
* @var string $defaultFileName The default file name for the feeds configuration.
*/
protected $defaultFileName = "config/feed-me/feeds.yaml";

// Public Methods
// =========================================================================

/**
* Writes the feeds data to a YAML file.
*
* @return bool True if the data was successfully written to the file, false otherwise.
* @throws \yii\base\InvalidConfigException If the plugin instance is not properly configured.
*/
public function write(): bool {
$feeds = array_map(
function($feed) { return ["id" => $feed->id] + $feed->getRecordAttributes(); },
Plugin::getInstance()->getFeeds()->getFeeds()
);

$fileName = $this->defaultFileName;
$fileContents = Yaml::dump($feeds, JSON_PRETTY_PRINT);

if(!file_exists($this->defaultFileName)) mkdir(dirname($this->defaultFileName), 0777, true);

return file_put_contents($fileName, $fileContents) !== false;
}

/**
* Reads feed data from a YAML file and imports each feed.
*
* @return \stdClass The result object containing success flag, successful feeds, and failed feeds.
* @throws \yii\base\InvalidConfigException If the configuration is invalid.
*/
public function read(): \stdClass {
$result = (object)["success" => true, "success_feeds" => [], "failed_feeds" => []];

// Delete all feeds
foreach(Plugin::getInstance()->getFeeds()->getFeeds() as $feed) {
Plugin::getInstance()->getFeeds()->deleteFeedById($feed['id']);
}

$fileName = $this->defaultFileName;
$feeds = Yaml::parse(file_get_contents($fileName));
foreach ($feeds as $feed) {
$record = new FeedRecord();
$record->setAttributes($feed, false);
$record->save(false);
$success = (bool) $record->id;
if ($success) {
$result->success_feeds[] = $record;
} else {
$result->failed_feeds[] = $record;
}
}

if(count($result->failed_feeds) > 0) {
$result->success = false;
}

return $result;
}
}
26 changes: 2 additions & 24 deletions src/services/Feeds.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,30 +119,8 @@ public function saveFeed(FeedModel $model, bool $runValidation = true): bool
}
}

$record->name = $model->name;
$record->feedUrl = $model->feedUrl;
$record->feedType = $model->feedType;
$record->primaryElement = $model->primaryElement;
$record->elementType = $model->elementType;
$record->siteId = $model->siteId;
$record->singleton = $model->singleton;
$record->duplicateHandle = $model->duplicateHandle;
$record->updateSearchIndexes = $model->updateSearchIndexes;
$record->paginationNode = $model->paginationNode;
$record->passkey = $model->passkey;
$record->backup = $model->backup;
$record->setEmptyValues = $model->setEmptyValues;

if ($model->elementGroup) {
$record->setAttribute('elementGroup', Json::encode($model->elementGroup));
}

if ($model->fieldMapping) {
$record->setAttribute('fieldMapping', Json::encode($model->fieldMapping));
}

if ($model->fieldUnique) {
$record->setAttribute('fieldUnique', Json::encode($model->fieldUnique));
foreach($model->getRecordAttributes() as $name => $value) {
$record->setAttribute($name, $value);
}

if ($isNewModel) {
Expand Down
10 changes: 5 additions & 5 deletions src/templates/_includes/elements/entries/column.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{% set sectionId = feed.elementGroup[elementType].section %}
{% set entryTypeId = feed.elementGroup[elementType].entryType %}
{% set sectionUid = feed.elementGroup[elementType].section %}
{% set entryTypeUid = feed.elementGroup[elementType].entryType %}

{% if sectionId and entryTypeId %}
{% set section = craft.app.sections.getSectionById(sectionId) %}
{% set entryType = craft.app.sections.getEntryTypeById(entryTypeId) %}
{% if sectionUid and entryTypeUid %}
{% set section = craft.app.sections.getSectionByUid(sectionUid) %}
{% set entryType = craft.app.sections.getEntryTypeByUid(entryTypeUid) %}

{% if section and entryType %}
<span class="element-group">{{ section.name }}</span>
Expand Down