core/Backend/Dynamic/GlobalModulesMenu.php
<?php
namespace Kontentblocks\Backend\Dynamic;
use Kontentblocks\Backend\Environment\PostEnvironment;
use Kontentblocks\Backend\EditScreens\ScreenManager;
use Kontentblocks\Backend\Storage\ModuleStorage;
use Kontentblocks\Kontentblocks;
use Kontentblocks\Language\I18n;
use Kontentblocks\Modules\ModuleWorkshop;
use Kontentblocks\Templating\CoreView;
use Kontentblocks\Utils\Utilities;
use Symfony\Component\HttpFoundation\Request;
/**
* Class GlobalModules
* @package Kontentblocks\Backend\Dynamic
*/
class GlobalModulesMenu
{
/**
* Class constructor
* Add relevant Hooks
* @since 0.1.0
*/
public function __construct()
{
add_action('init', array($this, 'registerPostType'));
add_action('init', array($this, 'registerPseudoArea'));
add_action('admin_menu', array($this, 'addAdminMenu'), 19);
add_action('edit_form_after_title', array($this, 'addForm'), 1);
add_action('save_post', array($this, 'save'), 11, 2);
add_filter('wp_insert_post_data', array($this, 'postData'), 10, 2);
add_filter('post_updated_messages', array($this, 'postTypeMessages'));
}
/**
* Add admin menu entry
*
* @since 0.1.0
* @return void
*/
public function addAdminMenu()
{
$i18n = I18n::getPackage('Modules');
if (!Utilities::adminMenuExists('Kontentblocks')) {
add_menu_page(
'kontentblocks',
'Kontentblocks',
'manage_kontentblocks_global_modules',
'/edit.php?post_type=kb-gmd',
false,
false
);
}
add_submenu_page(
'/edit.php?post_type=kb-dyar',
$i18n['global']['menuTitle'],
$i18n['global']['menuTitle'],
'manage_kontentblocks_global_modules',
'/edit.php?post_type=kb-gmd',
false
);
}
/**
* Handles which form to show
* either add new
* or the module form
* @since 0.1.0
* @return void
*/
public function addForm()
{
global $post;
wp_nonce_field('kontentblocks_save_post', 'kb_noncename');
wp_nonce_field('kontentblocks_ajax_magic', '_kontentblocks_ajax_nonce');
$storage = new ModuleStorage($post->ID);
// on this screen we always deal with only
// one module
$template = $storage->getModuleDefinition($post->post_name);
// no template yet, create new
if (empty($template)) {
$this->createForm();
} else {
$this->globalModule($template);
}
}
/**
* Form for creating new template
*
* @since 0.1.0
* @return void
*/
protected function createForm()
{
$screen = get_current_screen();
$request = Utilities::getRequest();
if ($screen->post_type !== 'kb-gmd') {
return;
}
/*
* Form validation happens on the frontend
* if this fails for any reason, data is preserved anyway
* for completeness
*/
$postData = $request->request->get('new-module', array());
// Data for twig
$templateData = array(
'modules' => $this->prepareModulesforSelectbox($postData),
'nonce' => wp_create_nonce('new-gmodule'),
'data' => $postData,
'strings' => I18n::getPackages('Common', 'Modules')
);
// To keep html out of php files as much as possible twig is used
// Good thing about twig is it handles unset vars gracefully
$formNew = new CoreView('global-modules/add-new.twig', $templateData);
$formNew->render(true);
}
/**
* Gets all available Modules from registry
*
* @param array $postData potential incomplete form data
*
* @since 0.1.0
* @return array
*/
private function prepareModulesforSelectbox($postData)
{
$type = (isset($postData['type'])) ? $postData['type'] : '';
$modules =
$modules = $this->getGloballyAllowed();
$collection = array();
if (!empty($modules)) {
foreach ($modules as $module) {
$collection[] = array(
'name' => $module['settings']['name'],
'class' => $module['settings']['class'],
'selected' => ($module['settings']['class'] === $type) ? 'selected="selected"' : ''
);
}
}
return $collection;
}
/**
* Filter all modules which may be created as a template
* @since 0.1.0
* @return array
*/
public function getGloballyAllowed()
{
/** @var \Kontentblocks\Modules\ModuleRegistry $registry */
$registry = Kontentblocks::getService('registry.modules');
return array_filter(
$registry->getAll(),
function ($module) {
if (isset($module['settings']['globalModule']) && $module['settings']['globalModule'] == true) {
return true;
}
return false;
}
);
}
/**
* Display form of the module
*
* @param $gmodule
* @since 0.1.0
* @return void|string
*/
protected function globalModule($gmodule)
{
global $post;
if (empty($gmodule)) {
wp_die('no template arg provided');
}
// TODO Include a public context switch
// Modules resp. the input form of a module may rely on a certain context
// or have different fields configuration
// TODO Explanation text for non-developers on page
$context = (isset($_GET['area-context'])) ? $_GET['area-context'] : 'normal';
// infamous hidden editor hack
Utilities::hiddenEditor();
// need to create a ew module here in order to override areaContext
$environment = Utilities::getPostEnvironment($post->ID);
$gmodule['areaContext'] = $context;
$workshop = new ModuleWorkshop($environment, $gmodule);
$module = $workshop->getModule();
if ($module === false) {
$formNew = new CoreView('global-modules/edit-gmodule-gone.twig', array(
'i18n' => I18n::getPackages('Common', 'Menus'),
'attachedTo' => $this->prepareAttachedTo()
));
return $formNew->render(true);
}
Kontentblocks::getService('utility.jsontransport')->registerModule($module->toJSON());
// Data for twig
$templateData = array(
'nonce' => wp_create_nonce('update-gmodule'),
'module' => $module,
'attachedTo' => $this->prepareAttachedTo(),
'contexts' => ScreenManager::getDefaultContextLayout(),
'strings' => I18n::getPackages('Common', 'Menus')
);
$return = filter_input(INPUT_GET, 'return', FILTER_VALIDATE_INT);
if (is_numeric($return)) {
echo "<input type='hidden' name='kb_return_to_post' value='{$return}' >";
}
// To keep html out of php files as much as possible twig is used
$formNew = new CoreView('global-modules/edit-gmodule.twig', $templateData);
$formNew->render(true);
}
/**
* @return array
* @since 0.2.0
*/
private function prepareAttachedTo()
{
global $wpdb;
$posts = array();
$meta = get_post_meta(get_the_ID(), '_kb_attached_to', true);
if (!is_array($meta)) {
return $posts;
}
$unique = implode(',', array_values($meta));
if (!empty($unique)) {
$posts = $wpdb->get_results("SELECT * FROM $wpdb->posts WHERE ID IN ('$unique') ");
}
return $posts;
}
/**
* Save handler, either creates a new module or just updates an exisiting
*
* @param int $postId
* @param \WP_POST $postObj
* @return bool|void
* @since 0.1.0
*/
public function save($postId, \WP_Post $postObj)
{
// auth request
if (!$this->auth($postId)) {
return false;
}
$value = Utilities::getRequest();
$environment = Utilities::getPostEnvironment($postId);
$moduleRepository = $environment->getModuleRepository();
$module = $moduleRepository->getModuleObject($postObj->post_name);
// no template yet, create an new one
if (!$module) {
$this->createGlobalModule($postId, $postObj, $environment);
} else {
if (!wp_verify_nonce($value->request->filter('_nonce', '', FILTER_SANITIZE_STRING), 'update-gmodule')) {
wp_die('Nonce verification failed');
}
// update existing
$old = $module->model->export();
$data = $value->request->get($module->getId());
$new = $module->save($data, $old);
$toSave = Utilities::arrayMergeRecursive($new, $old);
// save viewfile if present
$module->properties->viewfile = (!empty($data['viewfile'])) ? $data['viewfile'] : '';
$environment->getStorage()->saveModule($module->getId(), $toSave);
$environment->getStorage()->reset();
$environment->getStorage()->addToIndex($module->getId(), $module->properties->export());
// return to original post if the edit request came from outside
$redirect = $redirect = $value->request->getInt(
'kb_return_to_post', null
);
if ($redirect) {
$url = get_edit_post_link($redirect);
wp_redirect(html_entity_decode($url));
exit;
}
}
}
/**
* Various checks
*
* @param $postId
*
* @since 0.1.0
* @return bool
*/
private function auth($postId)
{
// verify if this is an auto save routine.
// If it is our form has not been submitted, so we dont want to do anything
if (empty($_POST)) {
return false;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return false;
}
// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
// if (!wp_verify_nonce($_POST['kb_noncename'], 'kontentblocks_save_post')) {
// return false;
// }
// Check permissions
if (!current_user_can('edit_post', $postId)) {
return false;
}
if (!current_user_can('edit_kontentblocks')) {
return false;
}
if (get_post_type($postId) == 'revision' && !Utilities::isPreview()) {
return false;
}
if (get_post_type($postId) !== 'kb-gmd') {
return false;
}
// checks passed
return true;
}
/**
* Create a new template from form data
*
* @param $postId
* @param PostEnvironment $environment
* @internal param ModuleStorage $Storage
* @since 0.1.0
*/
public function createGlobalModule($postId, \WP_Post $post, PostEnvironment $environment)
{
$value = Utilities::getRequest();
$gmodule = $value->get('new-gmodule');
if (empty($gmodule)) {
return;
}
if (!wp_verify_nonce($value->request->filter('_nonce', '', FILTER_SANITIZE_STRING), 'new-gmodule')) {
wp_die('Verification failed.');
}
// set defaults
$defaults = array(
'globalModule' => true,
'parentObjectId' => $postId,
'name' => null, // equals post_title
'id' => null, // equals post_name
'type' => null, // module class
);
// parse $_POST data
$data = wp_parse_args($value->request->get('new-gmodule'), $defaults);
// 2 good reasons to stop
if (is_null(
$value->request->filter('name', null, FILTER_SANITIZE_STRING) ||
is_null($value->request->filter('type', null, FILTER_SANITIZE_STRING))
)) {
wp_die('Missing arguments');
}
$workshop = new ModuleWorkshop(
$environment, array(
'globalModule' => true,
'parentObjectId' => $postId,
'area' => 'global-module',
'areaContext' => 'normal',
'class' => $data['type'],
'mid' => $post->post_name
)
);
$definition = $workshop->getDefinitionArray();
do_action('kb.create:module', $definition);
// add to post meta kb_kontentblocks
$environment->getStorage()->addToIndex($post->post_name, $definition);
}
/**
*
* Since all native wp controls are removed from this screen
* we need to manually save essential post data
*
* @param $data
* @param $post
* @return mixed
*
* @since 0.1.0
*/
public function postData($data, $post)
{
if ($post['post_type'] !== 'kb-gmd') {
return $data;
}
$request = Utilities::getRequest();
if (!$request->request->get('new-gmodule', false)) {
return $data;
}
$reqData = $request->request->get('new-gmodule', array());
if (empty($reqData)) {
return $data;
}
$title = filter_var($reqData['name'], FILTER_SANITIZE_STRING);
$slug = wp_unique_post_slug(
sanitize_title($title),
$post['ID'],
$post['post_status'],
$post['post_type'],
0
);
// no template data send
if (!isset($title, $slug)) {
return $data;
}
$data['post_title'] = $title;
$data['post_name'] = $slug;
return $data;
}
/**
* Register the template post type
*
* @since 0.1.0
* @return void
*/
public function registerPostType()
{
$labels = array(
'name' => _x('Global Module', 'post type general name', 'Kontentblocks'),
'singular_name' => _x('Global Module', 'post type singular name', 'Kontentblocks'),
'menu_name' => _x('Global Modules', 'admin menu', 'Kontentblocks'),
'name_admin_bar' => _x('Global Modules', 'add new on admin bar', 'Kontentblocks'),
'add_new' => _x('add New', 'book', 'Kontentblocks'),
'add_new_item' => __('add New global module', 'Kontentblocks'),
'new_item' => __('new global module', 'Kontentblocks'),
'edit_item' => __('edit global module', 'Kontentblocks'),
'view_item' => __('view global module', 'Kontentblocks'),
'all_items' => __('all global modules', 'Kontentblocks'),
'search_items' => __('search global modules', 'Kontentblocks'),
'parent_item_colon' => __('parent global modules:', 'Kontentblocks'),
'not_found' => __('No global modules found.', 'Kontentblocks'),
'not_found_in_trash' => __('No global modules found in Trash.', 'Kontentblocks'),
);
$args = array(
'labels' => $labels,
'public' => false,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => false,
'query_var' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'menu_position' => 999,
'supports' => null
);
register_post_type('kb-gmd', $args);
remove_post_type_support('kb-gmd', 'editor');
remove_post_type_support('kb-gmd', 'title');
}
/**
* Modify template specific messages
*
* @param $messages
*
* @since 0.1.0
* @return mixed
*/
public function postTypeMessages($messages)
{
$post = get_post();
$messages['kb-gmd'] = array(
0 => '', // Unused. Messages start at index 1.
1 => __('global modules updated.', 'Kontentblocks'),
2 => __('Custom field updated.', 'Kontentblocks'), // not used
3 => __('Custom field deleted.', 'Kontentblocks'), // not used
4 => __('global modules updated.', 'Kontentblocks'),
/* translators: %s: date and time of the revision */
5 => isset($_GET['revision']) ? sprintf(
__(
'global module restored to revision from %s',
'Kontentblocks'
),
wp_post_revision_title((int)$_GET['revision'], false)
) : false,
6 => __('global module published.', 'Kontentblocks'),
7 => __('global module saved.', 'Kontentblocks'),
8 => __('global module submitted.', 'Kontentblocks'),
9 => sprintf(
__('global module scheduled for: <strong>%1$s</strong>.', 'Kontentblocks'),
// translators: Publish box date format, see http://php.net/date
date_i18n(__('M j, Y @ G:i', 'Kontentblocks'), strtotime($post->post_date))
),
10 => __('global module draft updated.', 'Kontentblocks'),
);
return $messages;
}
/**
* @since 0.1.0
*/
public function registerPseudoArea()
{
\Kontentblocks\registerArea(
array(
'id' => 'global-module',
'internal' => true,
'dynamic' => false
)
);
}
}