File: //proc/self/cwd/wp-content/plugins/cornerstone/includes/classes/Services/Resolver.php
<?php
namespace Themeco\Cornerstone\Services;
use Themeco\Cornerstone\Plugin;
use Themeco\Cornerstone\Util\Factory;
use Themeco\Cornerstone\Documents\Document;
use Themeco\Cornerstone\Documents\DocumentCache;
use Themeco\Cornerstone\Elements\Renderer;
use Themeco\Cornerstone\Elements\ElementData;
class Resolver implements Service {
protected $renderStack = [];
protected $docTypeInfoCache = [];
protected $plugin;
protected $elementsService;
protected $permissions;
protected $env;
protected $documentCache;
protected $assignments;
protected $populatedDocTypes;
protected $layout_template;
protected $allowed;
public function __construct(Plugin $plugin, Elements $elements, Permissions $permissions, Env $env, DocumentCache $documentCache, Assignments $assignments ) {
$this->plugin = $plugin;
$this->elementsService = $elements;
$this->permissions = $permissions;
$this->env = $env;
$this->documentCache = $documentCache;
$this->assignments = $assignments;
}
public function setup() {
add_action('after_setup_theme', [ $this, 'attach' ] );
}
public function attach() {
if (did_action( 'cs_before_preview_frame_component' ) ) {
return;
}
add_action( 'init', array( $this, 'registerPostTypesAndStatuses' ) );
if ( $this->env->isSiteBuilder() ) {
add_action( 'template_redirect', array( $this, 'setup_views' ), 100 );
add_action( 'cs_late_template_redirect', array( $this, 'setup_header' ) );
add_action( 'cs_late_template_redirect', array( $this, 'setup_footer' ) );
add_filter( 'template_include', array( $this, 'resolve_layout_template'), 97 ); // before under construction extension
}
}
public function getDocument( $postOrIdOrDocument, $force = false ) {
if ($force) {
return $this->documentCache->get( $postOrIdOrDocument );
}
if ( is_a( $postOrIdOrDocument, Document::class ) ) {
return $postOrIdOrDocument;
}
return $this->documentCache->get( $postOrIdOrDocument );
}
public function makeElementData($id, $data) {
return $this->plugin->resolve(ElementData::class)->set($id, $data);
}
public function registerPostStatuses() {
}
public function registerPostTypesAndStatuses() {
register_post_status( 'tco-data', [
'internal' => true
] );
register_post_type( 'cs_global_block', array(
'labels' => array(
'name' => csi18n( "common.document.components" ),
'singular_name' => csi18n( "common.document.component" ),
),
'public' => false,
'capability_type' => 'page',
'supports' => false
) );
if ( $this->env->isSiteBuilder() ) {
register_post_type( 'cs_header', array(
'public' => false,
'capability_type' => 'page',
'supports' => false,
'labels' => array(
'name' => csi18n( "common.document.headers" ),
'singular_name' => csi18n( "common.document.header" ),
)
) );
register_post_type( 'cs_footer', array(
'public' => false,
'capability_type' => 'page',
'supports' => false,
'labels' => array(
'name' => csi18n( "common.document.footers" ),
'singular_name' => csi18n( "common.document.footer" ),
)
) );
register_post_type( 'cs_layout_single', array(
'public' => false,
'capability_type' => 'page',
'supports' => false,
'labels' => array(
'name' => csi18n( 'common.document.singles' ),
'singular_name' => csi18n( 'common.document.single' ),
)
) );
register_post_type( 'cs_layout_archive', array(
'public' => false,
'capability_type' => 'page',
'supports' => false,
'labels' => array(
'name' => csi18n( 'common.document.archives' ),
'singular_name' => csi18n( 'common.document.archive' ),
)
) );
//
// Legacy
//
register_post_type( 'cs_layout', array(
'public' => false,
'capability_type' => 'page',
'supports' => false,
'labels' => array(
'name' => csi18n( 'common.document.legacy-layouts' ),
'singular_name' => csi18n( 'common.document.legacy-layout' ),
)
) );
}
}
/**
* Migration from cs_layout to cs_layout_{type}
* Pro5 to Pro6
*/
public function migrateUntypedLayouts() {
// Before get_posts grab
do_action("cs_before_migrate_untyped_layouts_grab");
// ids of cs_layout
$ids = get_posts([
'post_type' => 'cs_layout',
'post_status' => ['tco-data'],
'fields' => 'ids',
]);
if (empty($ids)) {
return;
}
// Process ids
// Not used by WPML anymore
do_action("cs_before_migrate_untyped_layouts", $ids);
foreach ($ids as $id) {
$ignore = apply_filters("cs_before_migrate_untyped_ignore", false, $id);
if ($ignore) {
continue;
}
$doc = Document::locate( $id );
$newType = Document::resolvePostTypeForDocType($doc->getDocType());
// Use Raw SQL
if (
!defined("CS_MIGRATE_UNTYPED_USE_RAW_UPDATE")
|| !empty(CS_MIGRATE_UNTYPED_USE_RAW_UPDATE)
) {
$this->rawSQLPostTypeUpdate($id, $newType);
continue;
}
$insert = [
'ID' => $id,
'post_type' => $newType,
'post_status' => 'tco-data'
];
wp_update_post($insert, true);
}
// Post type has changed
do_action("cs_after_migrate_untyped_layouts");
}
public function setup_views() {
$this->setup_layouts();
// cs_output_header/cs_output_footer are false when
// - we are previewing a Global Block
// - the current Layout has the header/footer disabled
// - the current legacy page template contains "No Header/Footer"
if ( apply_filters( 'cs_output_header', true ) ) {
do_action( 'cs_connect_masthead' );
}
if ( apply_filters( 'cs_output_footer', true ) ) {
do_action( 'cs_connect_colophon' );
}
}
public function setup_layouts() {
$layout = apply_filters( 'cs_output_layout', true ) ? $this->assignments->get_active_layout() : null;
if ( ! is_null( $layout ) ) {
do_action('cs_will_output_layout', $layout );
do_action('cs_will_output_layout_' . $layout->getDocType(), $layout );
$layout_settings = $layout->settings();
if (!$layout_settings['header_enabled']) {
add_filter('cs_output_header', '__return_false' );
}
if (!$layout_settings['footer_enabled']) {
add_filter('cs_output_footer', '__return_false' );
}
if ($layout_settings['layout_type']) {
$this->loadDocument( $layout );
if (!did_action('cs_before_preview_frame_layout')) {
$this->deferRenderDocument( $layout, [ 'layout' => 'cs_layout' ] );
}
// these also match variants like single-wc and archive-wc
if ( strpos( $layout_settings['layout_type'], 'single' ) === 0 ) {
$this->layout_template = $this->plugin->path . '/includes/views/theming/layout-single.php';
}
if ( strpos( $layout_settings['layout_type'], 'archive' ) === 0 ) {
$this->layout_template = $this->plugin->path . '/includes/views/theming/layout-archive.php';
}
}
} else {
if ( is_singular() ) {
add_action( 'cs_layout', [ $this, 'auto_the_content' ] );
}
// see ThemeManagement::resolveTemplate for fallback templates
}
}
public function auto_the_content() {
$element_data = $this->makeElementData('content', [
[
'_region' => 'content',
'_type' => 'the-content'
]
])->decorated();
echo $this->elementsService->renderElements($element_data);
}
public function resolve_layout_template( $template ) {
return isset($this->layout_template) ? $this->layout_template : $template;
}
public function setup_footer() {
$footer = apply_filters( 'cs_output_footer', true ) ? $this->assignments->get_active_footer() : null;
if ( is_null( $footer ) ) {
return;
}
do_action('cs_will_output_footer', $footer);
$this->loadDocument( $footer );
if (!did_action('cs_before_preview_frame_footer')) {
$this->deferRenderDocument( $footer, [ 'footer' => 'cs_colophon' ]);
}
}
public function setup_header() {
$header = apply_filters( 'cs_output_header', true ) ? $this->assignments->get_active_header() : null;
if ( is_null( $header ) ) {
return;
}
do_action( 'cs_will_output_header', $header );
$this->loadDocument( $header );
if (!did_action('cs_before_preview_frame_header')) {
$this->deferRenderDocument( $header, [
'top' => 'cs_masthead',
'left' => 'cs_masthead',
'bottom' => 'cs_masthead',
'right' => 'cs_masthead',
]);
}
}
public function loadDocument( $document ) {
$doc = $this->getDocument( $document );
$this->plugin->service('Tss')->registerDocument( $doc );
$this->plugin->service('Styling')->addCustomCssFromDocument( $doc );
$this->plugin->service('EnqueueScripts')->addCustomJsFromDocument( $doc );
}
// Prevent a document from ever rendering itself recursively
public function safeDocRender( $doc, $fn ) {
if ( in_array( $doc->id(), $this->renderStack, true ) ) {
return '<!-- render loop -->';
}
$this->renderStack[] = $doc->id();
$result = $fn();
array_pop( $this->renderStack );
return $result;
}
public function getRenderStack() {
return $this->renderStack;
}
/**
* Render elements by post ID
* grab a document by ID and then render
*
* @param int $postId
*
* @return string
*/
public function renderContentByPostId($postId) {
$doc = $this->getDocument( $postId );
if (is_null($doc)) {
return '';
}
return $this->renderContentFromDocument($doc);
}
public function renderContentFromDocument( $doc ) {
return $this->safeDocRender($doc, function() use ($doc) {
$output = '';
try {
$this->loadDocument( $doc );
$output = $this->renderDocument($doc);
} catch ( \Exception $e ) {
trigger_error( $e->getMessage(), E_USER_WARNING );
}
return $output;
});
}
public function setupBarSpaces( $regions ) {
add_action('wp_body_open', function() use ($regions) {
// Hook in left and right bar spaces which are output earlier than their bars.
$bar_space_actions = array(
'left' => 'x_before_site_begin',
'right' => 'x_before_site_begin',
);
foreach ( $regions as $region ) {
foreach ( $region['_modules'] as $element ) {
if ($element['_type'] === 'bar') {
if ( isset( $bar_space_actions[ $element['_region']] ) ) {
if ( 'fixed' === cs_identity_bar_position( $element )) {
cs_defer_html(
$bar_space_actions[ $element['_region'] ],
cs_create_bar_space( $this->plugin->service('Tss')->applyTssToElement( $element ) )
);
}
}
}
}
}
});
}
public function deferRenderDocument( $document, $hooks = []) {
$regions = $document->getElementData()->decorated();
if ($document->getDocType() === 'layout:header') {
$this->setupBarSpaces( $regions );
}
foreach( $regions as $region ) {
if ( isset( $hooks[ $region['_region'] ] ) ) {
cs_action_defer( $hooks[ $region['_region'] ], function() use ($region, $document) {
echo $this->renderScoped( $region['_modules'], $document );
}, [], 10, true );
}
}
}
public function renderDocument( $document ) {
return $this->renderScoped( $document->getElementData()->decorated(), $document );
}
public function renderScoped( $elements, $document ) {
$frame = cornerstone('Vm')->runtime()->newFrame();
// PLACEHOLDER - This is where document specific Parameters can be loaded into the stack frame
// Parameter::defineParametersForRender($data, $frame, $document->id());
$result = $this->elementsService->renderElements( $elements );
$frame->dispose();
return do_shortcode( $result );
}
public function getDocumentLayoutTypes() {
$isApi = did_action('rest_api_init');
$layouts[] = [
'value' => 'layout:header',
'postType' => 'cs_header',
'assignmentContext' => 'any',
'labelSingular' => csi18n('common.document.header'),
'label' => csi18n('common.document.headers'),
'permissionContext' => 'layout',
'defaultTemplateGroup' => 'header'
];
$layouts[] = [
'value' => 'layout:footer',
'postType' => 'cs_footer',
'assignmentContext' => 'any',
'labelSingular' => csi18n('common.document.footer'),
'label' => csi18n('common.document.footers'),
'permissionContext' => 'layout',
'defaultTemplateGroup' => 'footer'
];
$layouts[] = [
'value' => 'layout:single',
'postType' => 'cs_layout_single',
'labelSingular' => csi18n('common.document.single'),
'label' => csi18n('common.document.singles'),
'permissionContext' => 'layout',
'defaultTemplateGroup' => 'blog'
];
$layouts[] = [
'value' => 'layout:archive',
'postType' => 'cs_layout_archive',
'labelSingular' => csi18n('common.document.archive'),
'label' => csi18n('common.document.archives'),
'permissionContext' => 'layout',
'defaultTemplateGroup' => 'blog'
];
$layouts = apply_filters('_cs_document_layout_types', $layouts, $this->permissions, $isApi);
return $layouts;
}
public function getDocumentTypeGroups() {
$groups = [];
$content = [];
$layouts = [];
$custom = [];
$isApi = did_action('rest_api_init');
$contentPostTypes = $this->permissions->getUserPostTypes();
$builtIn = [];
if ( $isApi || in_array( 'page', $contentPostTypes, true ) ) {
$builtIn[] = 'page';
}
if ( $isApi || in_array( 'post', $contentPostTypes, true ) ) {
$builtIn[] = 'post';
}
$contentTypes = array_unique( array_merge( $builtIn, $contentPostTypes ) ) ;
foreach ($contentTypes as $type) {
$post_type_obj = get_post_type_object( $type );
if ( ! $post_type_obj ) {
continue; // old permission of deactivated plugin
}
$labels = get_post_type_labels($post_type_obj);
$content[] = array(
'value' => 'content:' . $type,
'postType' => $type,
'permissionContext' => 'content.' . $type,
'defaultTemplateGroup' => 'page',
'labelSingular' => $labels->singular_name,
'label' => $labels->name
);
}
if ( ($isApi || $this->permissions->userCan( 'layout' ) ) && $this->env->isSiteBuilder() ) {
$layouts = $this->getDocumentLayoutTypes();
}
if ( $isApi || $this->permissions->userCan( 'component' ) ) {
$custom[] = [
'value' => 'custom:component',
'postType' => 'cs_global_block',
'labelSingular' => csi18n('common.document.component'),
'label' => csi18n('common.document.components'),
'permissionContext' => 'component'
];
}
if ( count( $content ) > 0 ) $groups[] = [ 'name' => 'content', 'label' => csi18n('common.document.content'), 'options' => $content ];
if ( count( $layouts ) > 0 ) $groups[] = [ 'name' => 'layout', 'label' => csi18n('common.document.layouts'), 'options' => $layouts ];
if ( count( $custom ) > 0 ) $groups[] = [ 'name' => 'custom', 'label' => csi18n('common.document.custom'), 'options' => $custom ];
return $groups;
}
public function getPopulatedDocumentTypeGroups() {
if ( ! isset( $this->populatedDocTypes ) ) {
$this->populatedDocTypes = array_map(function( $group ) {
$group['options'] = array_map(function($option){
$option['portableSettings'] = [
'customCSS',
'customJS',
];
// 'portableSettings' => ['customCSS', 'customJS', 'layout_type', 'header_enabled', 'footer_enabled'],
$isContent = strpos($option['value'], 'content:') === 0;
$isLayout = strpos($option['value'], 'layout:') === 0;
$isCustom = strpos($option['value'], 'custom:') === 0;
if ( $isContent ) {
$option['portableSettings'][] = 'general_allow_comments';
$option['portableSettings'][] = 'general_manual_excerpt';
$option['portableSettings'][] = 'general_page_template';
}
if ($isContent || $isCustom) {
$option['titleKey'] = 'general_post_title';
$option['regions'] = [ 'content' ];
}
if ($isCustom) {
$option['portableSettings'][] = 'document_visibility';
}
if ($isLayout ) {
$option['titleKey'] = 'general_title';
$option['regions'] = [ 'layout' ];
$option['portableSettings'][] = 'layout_type';
$option['portableSettings'][] = 'header_enabled';
$option['portableSettings'][] = 'footer_enabled';
$isHeader = $option['value'] === 'layout:header';
$isFooter = $option['value'] === 'layout:footer';
$isSingle = $option['value'] === 'layout:single';
$isArchive = $option['value'] === 'layout:archive';
if ($isHeader) {
$option['portableSettings'][] = 'multi_region';
$option['regions'] = [ 'top', 'left', 'right', 'bottom' ];
}
if ($isFooter) {
$option['portableSettings'][] = 'multi_region';
$option['regions'] = [ 'footer' ];
}
if ( $isHeader || $isFooter) {
$option['defaultPreviewUrl'] = home_url();
}
if ( $isSingle ) {
$posts = get_posts( ['numberposts' => 1, 'post_type' => 'post' ] ); // find first post
if ( empty( $posts ) ) {
$posts = get_posts( ['numberposts' => 1, 'post_type' => 'page' ] ); // use a page if there are no posts
}
$option['defaultPreviewUrl'] = empty($posts[0]) ? home_url() : get_permalink( $posts[0]->ID );
}
if ( $isArchive ) {
$blog_page_id = get_option( 'page_for_posts' );
$option['defaultPreviewUrl'] = $blog_page_id ? get_permalink( $blog_page_id ) : home_url();
}
}
return apply_filters('_cs_document_layout_types_populate', $option);
}, $group['options']);
return $group;
}, $this->getDocumentTypeGroups() );
}
return $this->populatedDocTypes;
}
public function findDocTypeInfo( $type ) {
$groups = $this->getPopulatedDocumentTypeGroups();
foreach ($groups as $group) {
foreach( $group['options'] as $option ) {
if ( $option['value'] === $type ) {
return $option;
}
}
}
return null;
}
public function getDocTypeInfo( $type ) {
if ( ! isset( $this->docTypeInfoCache[ $type ] ) ) {
$this->docTypeInfoCache[ $type ] = $this->findDocTypeInfo( $type );
}
return $this->docTypeInfoCache[ $type ];
}
/**
* Get option value for all groups registered as documents
*/
public function getDocOptionInfo($docKey = 'value') {
$groups = $this->getDocumentTypeGroups();
$types = [];
foreach ($groups as $group) {
foreach ($group['options'] as $option) {
// Error check
if (empty($option[$docKey])) {
trigger_error('Key not found on group ' . $group . ' : ' . $docKey);
continue;
}
$types[] = $option[$docKey];
}
}
return $types;
}
/**
* Allowed doc types in cornerstone layout:footer format
*/
public function getAllowedDocTypes() {
if ( ! isset( $this->allowed ) ) {
$this->allowed = $this->getDocOptionInfo('value');
}
return $this->allowed;
}
/**
* Gets the document option of 'postType'
*/
public function getAllowedPostTypes() {
return $this->getDocOptionInfo('postType');
}
public function clearDocumentCache($id) {
$this->documentCache->unset($id);
}
/**
* Use Raw SQL as update
*/
private function rawSQLPostTypeUpdate($id, $postType) {
global $wpdb;
$table = esc_sql("{$wpdb->prefix}posts");
// Update icl_translations
$sql = "UPDATE $table
SET post_type = %s
WHERE ID = %d
";
$prepared = $wpdb->prepare($sql, $postType, $id);
// Update Query
$wpdb->query($prepared);
}
}