connectors/class-connector-editor.php
<?php
/**
* Connector for Editor
*
* @package WP_Stream
*/
namespace WP_Stream;
/**
* Class - Connector_Editor
*/
class Connector_Editor extends Connector {
/**
* Connector slug
*
* @var string
*/
public $name = 'editor';
/**
* Actions registered for this connector
*
* @var array
*/
public $actions = array();
/**
* Actions registered for this connector
*
* @var array
*/
private $edited_file = array();
/**
* Register connector in the WP Frontend
*
* @var bool
*/
public $register_frontend = false;
/**
* Register all context hooks
*
* @return void
*/
public function register() {
parent::register();
add_action( 'load-theme-editor.php', array( $this, 'get_edition_data' ) );
add_action( 'load-plugin-editor.php', array( $this, 'get_edition_data' ) );
add_filter( 'wp_redirect', array( $this, 'log_changes' ) );
}
/**
* Return translated connector label
*
* @return string Translated connector label
*/
public function get_label() {
return esc_html__( 'Editor', 'stream' );
}
/**
* Return translated action labels
*
* @return array Action label translations
*/
public function get_action_labels() {
return array(
'updated' => esc_html__( 'Updated', 'stream' ),
);
}
/**
* Return translated context labels
*
* @return array Context label translations
*/
public function get_context_labels() {
/**
* Filter available context labels for the Editor connector
*
* @return array Array of context slugs and their translated labels
*/
return apply_filters(
'wp_stream_editor_context_labels',
array(
'themes' => esc_html__( 'Themes', 'stream' ),
'plugins' => esc_html__( 'Plugins', 'stream' ),
)
);
}
/**
* Get the context based on wp_redirect location
*
* @param string $location The URL of the redirect.
*
* @return string Context slug
*/
public function get_context( $location ) {
$context = null;
if ( false !== strpos( $location, 'theme-editor.php' ) ) {
$context = 'themes';
}
if ( false !== strpos( $location, 'plugin-editor.php' ) ) {
$context = 'plugins';
}
/**
* Filter available contexts for the Editor connector
*
* @param string $context Context slug
* @param string $location The URL of the redirect
* @return string Context slug
*/
return apply_filters( 'wp_stream_editor_context', $context, $location );
}
/**
* Get the message format for file updates
*
* @return string Translated string
*/
public function get_message() {
/* translators: %1$s: a file name, %2$s: a theme / plugin name (e.g. "index.php", "Stream") */
return _x(
'"%1$s" in "%2$s" updated',
'1: File name, 2: Theme/plugin name',
'stream'
);
}
/**
* Add action links to Stream drop row in admin list screen
*
* @filter wp_stream_action_links_{connector}
*
* @param array $links Previous links registered.
* @param object $record Stream record.
*
* @return array Action links
*/
public function action_links( $links, $record ) {
if ( current_user_can( 'edit_theme_options' ) ) {
$file_name = $record->get_meta( 'file', true );
$file_path = $record->get_meta( 'file_path', true );
if ( ! empty( $file_name ) && ! empty( $file_path ) ) {
$theme_slug = $record->get_meta( 'theme_slug', true );
$plugin_slug = $record->get_meta( 'plugin_slug', true );
$theme_exists = ( ! empty( $theme_slug ) && file_exists( $file_path ) );
$plugin_exists = ( ! empty( $plugin_slug ) && file_exists( $file_path ) );
if ( $theme_exists ) {
$links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
array(
'theme' => rawurlencode( $theme_slug ),
'file' => rawurlencode( $file_name ),
),
self_admin_url( 'theme-editor.php' )
);
$links[ esc_html__( 'Theme Details', 'stream' ) ] = add_query_arg(
array(
'theme' => rawurlencode( $theme_slug ),
),
self_admin_url( 'themes.php' )
);
}
if ( $plugin_exists ) {
$links[ esc_html__( 'Edit File', 'stream' ) ] = add_query_arg(
array(
'plugin' => rawurlencode( $plugin_slug ),
'file' => rawurlencode( str_ireplace( trailingslashit( WP_PLUGIN_DIR ), '', $file_path ) ),
),
self_admin_url( 'plugin-editor.php' )
);
}
}
}
return $links;
}
/**
* Retrieves data submitted on the screen, and prepares it for the appropriate context type
*
* @action load-theme-editor.php
* @action load-plugin-editor.php
*/
public function get_edition_data() {
if (
(
isset( $_SERVER['REQUEST_METHOD'] )
&&
'POST' !== sanitize_text_field( $_SERVER['REQUEST_METHOD'] )
)
||
'update' !== wp_stream_filter_input( INPUT_POST, 'action' )
) {
return;
}
$theme_slug = wp_stream_filter_input( INPUT_POST, 'theme' );
if ( $theme_slug ) {
$this->edited_file = $this->get_theme_data( $theme_slug );
}
$plugin_slug = wp_stream_filter_input( INPUT_POST, 'plugin' );
if ( $plugin_slug ) {
$this->edited_file = $this->get_plugin_data( $plugin_slug );
}
}
/**
* Retrieve theme data needed for the log message
*
* @param string $slug The theme slug (e.g. twentyfourteen).
*
* @return mixed $output Compacted variables
*/
public function get_theme_data( $slug ) {
$theme = wp_get_theme( $slug );
if ( ! $theme->exists() || ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) ) {
return false;
}
$allowed_files = $theme->get_files( 'php', 1 );
$style_files = $theme->get_files( 'css' );
$file = wp_stream_filter_input( INPUT_POST, 'file' );
$allowed_files['style.css'] = $style_files['style.css'];
if ( empty( $file ) ) {
$file_name = 'style.css';
$file_path = $allowed_files['style.css'];
} else {
$file_name = $file;
$file_path = sprintf( '%s/%s', $theme->get_stylesheet_directory(), $file_name );
}
$file_md5 = md5_file( $file_path );
$name = $theme->get( 'Name' );
$output = compact(
'file_name',
'file_path',
'file_md5',
'slug',
'name'
);
return $output;
}
/**
* Retrieve plugin data needed for the log message
*
* @param string $slug The plugin file base name (e.g. akismet/akismet.php).
* @return mixed $output Compacted variables.
*/
public function get_plugin_data( $slug ) {
$base = null;
$name = null;
$slug = current( explode( '/', $slug ) );
$file_name = wp_stream_filter_input( INPUT_POST, 'file' );
$file_path = WP_PLUGIN_DIR . '/' . $file_name;
$file_md5 = md5_file( $file_path );
$plugins = get_plugins();
foreach ( $plugins as $key => $plugin_data ) {
if ( 0 === strpos( $key, $slug ) ) {
$base = $key;
$name = $plugin_data['Name'];
break;
}
}
$file_name = str_ireplace( trailingslashit( $slug ), '', $file_name );
$slug = ! empty( $base ) ? $base : $slug;
$output = compact(
'file_name',
'file_path',
'file_md5',
'slug',
'name'
);
return $output;
}
/**
* Logs changes
*
* @filter wp_redirect
*
* @param string $location Location.
*/
public function log_changes( $location ) {
if ( ! empty( $this->edited_file ) ) {
// TODO: phpcs fix.
if ( md5_file( $this->edited_file['file_path'] ) !== $this->edited_file['file_md5'] ) {
$context = $this->get_context( $location );
switch ( $context ) {
case 'themes':
$name_key = 'theme_name';
$slug_key = 'theme_slug';
break;
case 'plugins':
$name_key = 'plugin_name';
$slug_key = 'plugin_slug';
break;
default:
$name_key = 'name';
$slug_key = 'slug';
}
$this->log(
$this->get_message(),
array(
'file' => (string) $this->edited_file['file_name'],
$name_key => (string) $this->edited_file['name'],
$slug_key => (string) $this->edited_file['slug'],
'file_path' => (string) $this->edited_file['file_path'],
),
null,
$context,
'updated'
);
}
}
return $location;
}
}