Skip to content
This repository has been archived by the owner on May 9, 2023. It is now read-only.

Support for Singletons #31

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,66 @@ composer.phar
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
composer.lock

# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/
45 changes: 31 additions & 14 deletions Controller/RestApi.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space?


namespace Moderation\Controller;

Expand All @@ -15,7 +15,7 @@ class RestApi extends Controller {
];

/**
* Run schedulling.
* Run scheduling.
*/
public function list() {
$range = $this->param('range', 10);
Expand Down Expand Up @@ -45,38 +45,55 @@ public function list() {
}

/**
* Run schedulling.
* Run scheduling.
*/
public function run() {
$results = $this->list();
$processed = [];
$collections = [];
$data = [];
$entries = [];
foreach ($results['active'] as $data) {
$type = $data['schedule']['type'];
if (!isset($collections[$data['_collection']])) {
$collection = $this->app->module('collections')->collection($data['_collection']);
$collections[$data['_collection']] = $collection;
foreach ($results['active'] as $result) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably could we decouple better singletons from collections functionality, and reduce the size of the run function

$type = $result['schedule']['type'];
if (isset($result['_collection'])) {
if (isset($data[$result['_collection']])) {
$item = $data[$result['_collection']];
}
else {
$item = $this->app->module('collections')->collection($result['_collection']);
$data[$result['_collection']] = $item;
}
$entry = (array) $this->app->storage->findOne("collections/{$item['_id']}", ['_id' => $result['_oid']]);
}
else {
$collection = $collections[$data['_collection']];
if (isset($data[$result['_singleton']])) {
$item = $data[$result['_singleton']];
}
else {
$item = $this->app->module('singletons')->singleton($result['_singleton']);
$data[$result['_singleton']] = $item;
}
$entry = (array) $this->app->storage->findOne('singletons', ['filter' => ['_id' => $result['_oid']]);
}

$entry = (array) $this->app->storage->findOne("collections/{$collection['_id']}", ['_id' => $data['_oid']]);
$field = $data['_field'];
if ($data['_lang']) {
$field .= "_{$data['_lang']}";
}
if (isset($entry[$field]) && isset($this->moderation[$type])) {
$old_status = $entry[$field];
$entry[$field] = $this->moderation[$type];
$this->app->module('collections')->save($data['_collection'], $entry, ['revision' => TRUE]);
$this->app->storage->remove('moderation/schedule', ['_id' => $data['_id']]);
if (isset($result['_collection'])) {
$this->app->module('collections')->save($result['_collection'], $entry, ['revision' => TRUE]);
}
else {
$this->app->module('singletons')->saveData($result['_singleton'], $entry, ['revision' => TRUE]);
}
$this->app->storage->remove('moderation/schedule', ['_id' => $result['_id']]);
$processed[] = [
'_id' => $entry['_id'],
'old_status' => $old_status,
'new_status' => $entry[$field],
'schedule_data' => $data,
'schedule_data' => $result,
];
}
}
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Cockpit CMS Moderation Add-on

This addon extends Cockpit CMS core functionality by introducing the possibility to have moderation of collections. It means that its possible to create collections with a status (Unpublished, Draft or Published) affecting the way that entries are retrieved:
This addon extends Cockpit CMS core functionality by introducing the possibility to have moderation of collections and singletons. It means that its possible to create collections and singletons with a status (Unpublished, Draft or Published) affecting the way that entries are retrieved:

- **Unpublished** - Any collection entry in unpublished state will be filtered out.
- **Draft** - Any collection entry in Draft that doesn't have a previous revision in published status will be also filtered out. If there is a previous revision with published status the revision will be returned instead. However on a scenario that we have a published > unpublished > draft sequence no entry will be returned.
- **Unpublished** - Any collection entry or singleton in unpublished state will be filtered out.
- **Draft** - Any collection entry or singleton in Draft that doesn't have a previous revision in published status will be also filtered out. If there is a previous revision with published status the revision will be returned instead. However on a scenario that we have a published > unpublished > draft sequence no entry will be returned.
- **Published** - They are always returned.

## Installation
Expand Down
100 changes: 95 additions & 5 deletions actions.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* @file
* Implements moderation related actions on cockpit collections.
* Implements moderation related actions on cockpit collections and singletons.
*/

/**
Expand Down Expand Up @@ -34,7 +34,7 @@
$options['fields'][$field_name] = 1;
}

$app->trigger("moderation.find.before", [$name, &$options]);
$app->trigger("moderation.collections.find.before", [$name, &$options]);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change, would be possible to maintain the moderation.find.before hook and then call the functionality depending on what we have there? its fine on having the new hooks, just concerned on removing the existing one as any addons that rely on it will break

});

/**
Expand All @@ -50,7 +50,7 @@
}

// Get the moderation field.
$field = $app->module('moderation')->getModerationField($name);
$field = $app->module('moderation')->getModerationField('collection', $name);

if (!$field) {
return;
Expand Down Expand Up @@ -79,10 +79,10 @@
$moderation_field .= "_{$lang}";
}
// However, this has not been filtered:
$published = $app->module('moderation')->getLastPublished($entry['_id'], $moderation_field, $revisions);
$published = $app->module('moderation')->getLastPublished($moderation_field, $revisions);

if ($published) {
$published = $app->module('moderation')->removeLangSuffix($name, $published, $lang, $ignoreDefaultFallback);
$published = $app->module('moderation')->removeLangSuffix('collection', $name, $published, $lang, $ignoreDefaultFallback);
$published = array_merge($entry, array_intersect_key($published, $entry));
$published = [$published];
$populated = cockpit_populate_collection($published, $populate);
Expand All @@ -97,3 +97,93 @@
// Rebuild array indices.
$entries = array_values($entries);
});

/**
* Exclude unpublished singletons.
*/
$app->on('singleton.getData.before', function ($singleton, &$options) use ($app) {

// Exclude on unpublished state.
foreach ($singleton['fields'] as $field) {
if ($field['type'] === 'moderation') {
$field_name = $field_name_lang = $field['name'];
if ($field['localize'] && $lang = $app->param("lang", false)) {
$field_name_lang .= "_{$lang}";
}

$options['filter']['$and'][] = [$field_name_lang => ['$exists' => TRUE]];
$options['filter']['$and'][] = [$field_name_lang => ['$ne' => 'Unpublished']];
break;
}
}

if (!isset($field_name)) {
return;
}

// If we are using filter/fields we need to use original field name.
if (!empty($options['fields'])) {
$options['fields'][$field_name] = 1;
}

$app->trigger("moderation.singleton.getData.before", [$singleton, &$options]);
});

/**
* Iterate over the singleton.
*
* For the draft ones check if we have a previous published revision.
*/
$app->on('singleton.getData.after', function ($singleton, &$data) use ($app) {
$token = $app->param('previewToken', FALSE);
// If we have a valid previewToken don't need to go further.
if ($token && $app->module('moderation')->validateToken($token)) {
return;
}

// Get the moderation field.
$field = $app->module('moderation')->getModerationField('singleton', $singleton['name']);

if (!$field) {
return;
}

$lang = $app->param('lang', FALSE);
$ignoreDefaultFallback = $app->param('ignoreDefaultFallback', FALSE);
if ($ignoreDefaultFallback = $this->param('ignoreDefaultFallback', false)) {
$ignoreDefaultFallback = \in_array($ignoreDefaultFallback, ['1', '0']) ? \boolval($ignoreDefaultFallback) : $ignoreDefaultFallback;
}
$moderation_field = $field['name'];
$localize = $field['localize'] ?? FALSE;
$populate = $app->param('populate', 1);

if (!isset($data[$moderation_field])) {
return;
}

// If Draft ensure we retrieve the latest published revision.
// Please note that the singleton being checked has already been thru lang filtering.
if ($data[$moderation_field] == 'Draft') {
$revisions = $app->helper('revisions')->getList($singleton['_id']);

if ($lang && $localize) {
$moderation_field .= "_{$lang}";
}
// However, this has not been filtered:
$published = $app->module('moderation')->getLastPublished($moderation_field, $revisions);

if ($published) {
$published = $app->module('moderation')->removeLangSuffix('singleton', $singleton, $published, $lang, $ignoreDefaultFallback);
$published = array_merge($data, array_intersect_key($published, $data));
$published = [$published];
$populated = cockpit_populate_collection($published, $populate);
$data = $published[0] ? $published[0] : new stdClass();
}
else {
unset($data);
}
}
else if ($data[$moderation_field] == 'Unpublished') {
$data = new stdClass();
}
});
16 changes: 14 additions & 2 deletions admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,19 @@

$scheduleEnabled = $canSchedule && isset($settings['schedule']) && ($settings['schedule'] === '*' || in_array($name, $settings['schedule']));

$this->renderView("moderation:views/partials/entry-aside.php", ['enabled' => $scheduleEnabled]);
$this->renderView("moderation:views/partials/collections.entry.aside.php", ['enabled' => $scheduleEnabled]);
});

/**
* Add moderation markup to singleton sidebar.
*/
$this->on('singletons.form.aside', function($name) use ($app) {
$canSchedule = $app->module('cockpit')->hasaccess('moderation', ['manage', 'schedule']);
$settings = $this->retrieve('config/moderation', ['schedule' => []]);

$scheduleEnabled = $canSchedule && isset($settings['schedule']) && ($settings['schedule'] === '*' || in_array($name, $settings['schedule']));

$this->renderView("moderation:views/partials/singletons.form.aside.php", ['enabled' => $scheduleEnabled]);
});

/**
Expand Down Expand Up @@ -51,7 +63,7 @@
});

/**
* Provide modififications on the preview url (Helpers addon).
* Provide modifications on the preview url (Helpers addon).
*/
$this->on('helpers.preview.url', function(&$preview) use ($app) {
$keys = $app->module('cockpit')->loadApiKeys();
Expand Down
Loading