File manager - Edit - /home/theblueo/tv/fb4e3b/mvc.tar
Back
PostParams.php 0000666 00000004247 15213277215 0007366 0 ustar 00 <?php /** * Postdata wrapper */ class Loco_mvc_PostParams extends Loco_mvc_ViewParams { /** * @var Loco_mvc_PostParams */ private static $singleton; /** * Get actual postdata, not hacked postdata WordPress ruined with wp_magic_quotes * @return Loco_mvc_PostParams */ public static function get(){ if( ! self::$singleton ){ self::$singleton = self::create(); } return self::$singleton; } /** * @return void */ public static function destroy(){ self::$singleton = null; } /** * Construct clean postdata from current HTTP request * @return Loco_mvc_PostParams */ public static function create(){ $post = array(); if( 'POST' === $_SERVER['REQUEST_METHOD'] ){ // attempt to use clean input if available and unslashed if( ( $raw = file_get_contents('php://input') ) && ! get_magic_quotes_gpc() && ! get_magic_quotes_runtime() ){ parse_str( $raw, $post ); } // else reverse wp_magic_quotes (assumes no other process has hacked the array) else { $post = stripslashes_deep( $_POST ); } } return new Loco_mvc_PostParams( $post ); } /** * Construct postdata from a series of value pairs. * This is used in tests to simulate how a form is serialized and posted * * @return Loco_mvc_PostParams */ public static function fromSerial( array $serial ){ $pairs = array(); foreach( $serial as $pair ){ $pairs[] = rawurlencode($pair[0]).'='.rawurlencode($pair[1]); } parse_str( implode('&',$pairs), $parsed ); return new Loco_mvc_PostParams( $parsed ); } /** * Collapse nested array down to series of scalar forms * @return array */ public function getSerial(){ $serial = array(); $query = http_build_query( $this->getArrayCopy(), false, '&' ); foreach( explode('&',$query) as $str ){ $serial[] = array_map( 'urldecode', explode( '=', $str, 2 ) ); } return $serial; } } AjaxRouter.php 0000666 00000014553 15213277215 0007362 0 ustar 00 <?php /** * Handles execution of Ajax actions and rendering of JSON */ class Loco_mvc_AjaxRouter extends Loco_hooks_Hookable { /** * Current ajax controller * @var Loco_mvc_AjaxController */ private $ctrl; /** * @var Loco_output_Buffer */ private $buffer; /** * Generate a GET request URL containing required routing parameters * @return string */ public static function generate( $route, array $args = array() ){ // validate route autoload if debugging if( loco_debugging() ){ class_exists( self::routeToClass($route) ); } $args += array ( 'route' => $route, 'action' => 'loco_ajax', 'loco-nonce' => wp_create_nonce($route), ); return admin_url('admin-ajax.php','relative').'?'.http_build_query($args,null,'&'); } /** * Create a new ajax router and starts buffering output immediately */ public function __construct(){ $this->buffer = Loco_output_Buffer::start(); parent::__construct(); } /** * "init" action callback. * early-ish hook that ensures controllers can initialize */ public function on_init(){ try { $class = self::routeToClass( $_REQUEST['route'] ); // autoloader will throw error if controller class doesn't exist $this->ctrl = new $class; $this->ctrl->_init( $_REQUEST ); // do_action('loco_controller_init', $this->ctrl ); } catch( Loco_error_Exception $e ){ $this->ctrl = null; // throw $e; // <- debug } } /** * @return string */ private static function routeToClass( $route ){ $route = explode( '-', $route ); // convert route to class name, e.g. "foo-bar" => "Loco_ajax_foo_BarController" $key = count($route) - 1; $route[$key] = ucfirst( $route[$key] ); return 'Loco_ajax_'.implode('_',$route).'Controller'; } /** * Common ajax hook for all Loco admin JSON requests * @codeCoverageIgnore */ public function on_wp_ajax_loco_json(){ $json = $this->renderAjax(); // avoid outputing junk in JSON stream Loco_output_Buffer::clear(); Loco_output_Buffer::check(); // output stream is clear, we can flush JSON header('Content-Length: '.strlen($json), true ); header('Content-Type: application/json; charset=UTF-8', true ); // avoid hijacking of exit via wp_die_ajax_handler. Tests call renderAjax directly. echo $json; exit(0); } /** * Additional ajax hook for download actions that won't be JSON * @codeCoverageIgnore */ public function on_wp_ajax_loco_download(){ $data = $this->renderDownload(); if( is_string($data) ){ $path = ( $this->ctrl ? $this->ctrl->get('path') : '' ) or $path = 'error.json'; $file = new Loco_fs_File( $path ); $ext = $file->extension(); } else if( $data instanceof Exception ){ $data = sprintf('%s in %s:%u', $data->getMessage(), basename($data->getFile()), $data->getLine() ); $ext = null; } else { $data = (string) $data; $ext = null; } // set content type header appropriate for supported file extensions if( ! headers_sent() ){ $mimes = array( 'mo' => 'application/x-gettext-translation', 'po' => 'application/x-gettext', 'pot' => 'application/x-gettext', 'xml' => 'text/xml', 'json' => 'application/json', ); if( $ext && isset($mimes[$ext]) ){ header('Content-Type: '.$mimes[$ext].'; charset=UTF-8', true ); header('Content-Disposition: attachment; filename='.$file->basename(), true ); } else { header('Content-Type: text/plain; charset=UTF-8', true ); } header('Content-Length: '.strlen($data), true ); } // avoid hijacking of exit via wp_die_ajax_handler. Tests call renderDownload directly. echo $data; exit(0); } /** * Execute ajax controller to render JSON response body * @return string */ public function renderAjax(){ try { // respond with deferred failure from initAjax if( ! $this->ctrl ){ $route = isset($_REQUEST['route']) ? $_REQUEST['route'] : ''; throw new Loco_error_Exception( sprintf( __('Ajax route not found: "%s"','loco'), $route ) ); } // else execute controller to get json output $json = $this->ctrl->render(); if( is_null($json) || ! isset($json{0}) ){ throw new Loco_error_Exception( __('Ajax controller returned empty JSON','loco') ); } } catch( Loco_error_Exception $e ){ $json = json_encode( array( 'error' => $e->jsonSerialize(), 'notices' => Loco_error_AdminNotices::destroyAjax() ) ); } catch( Exception $e ){ $e = new Loco_error_Exception( $e->getMessage(), $e->getCode() ); $json = json_encode( array( 'error' => $e->jsonSerialize(), 'notices' => Loco_error_AdminNotices::destroyAjax() ) ); } if( $this->buffer ){ $this->buffer->close(); $this->buffer = null; } return $json; } /** * Execute ajax controller to render something other than JSON * @return string|Exception */ public function renderDownload(){ try { // respond with deferred failure from initAjax if( ! $this->ctrl ){ throw new Loco_error_Exception( __('Download action not found','loco') ); } // else execute controller to get raw output $data = $this->ctrl->render(); if( is_null($data) || ! isset($data{0}) ){ throw new Loco_error_Exception( __('Download controller returned empty output','loco') ); } } catch( Exception $e ){ $data = $e; } if( $this->buffer ){ $this->buffer->close(); $this->buffer = null; } return $data; } } AdminController.php 0000666 00000015711 15213277215 0010367 0 ustar 00 <?php /** * */ abstract class Loco_mvc_AdminController extends Loco_mvc_Controller { /** * @var Loco_mvc_View */ private $view; /** * Debugging timestamp (microseconds) * @var float */ private $bench; /** * Base url to plugin folder for web access * @var string */ private $baseurl; /** * Pre-init call invoked by router * @return Loco_mvc_AdminController */ final public function _init( array $args ){ if( loco_debugging() ){ $this->bench = microtime( true ); } $this->view = new Loco_mvc_View( $args ); $this->auth(); // check essential extensions on all pages so admin notices are shown foreach( array('json','mbstring') as $ext ){ loco_check_extension($ext); } // add contextual help tabs to current screen if there are any if( $screen = get_current_screen() ){ $this->view->cd('/admin/help'); $tabs = $this->getHelpTabs(); // always append common help tabs $tabs[ __('Help & support','loco') ] = $this->view->render('tab-support'); // set all tabs and common side bar $i = 0; foreach( $tabs as $title => $content ){ $id = sprintf('loco-help-%u', $i++ ); $screen->add_help_tab( compact('id','title','content') ); } $screen->set_help_sidebar( $this->view->render('side-bar') ); $this->view->cd('/'); } // helper properties for loading static resources $this->baseurl = plugins_url( '', loco_plugin_self() ); // add common admin page resources $this->enqueueStyle('admin', array('wp-jquery-ui-dialog') ); // load colour scheme is user has non-default $skin = get_user_option('admin_color'); if( $skin && 'fresh' !== $skin ){ $this->enqueueStyle( 'skins/'.$skin ); } // core minimized admin.js loaded on all pages before any other Loco scripts $this->enqueueScript('min/admin', array('jquery-ui-dialog') ); $this->init(); return $this; } /** * Post-construct initializer that may be overridden by child classes * @return void */ public function init(){ } /** * "admin_title" filter, modifies HTML document title if we've set one */ public function filter_admin_title( $admin_title, $title ){ if( $view_title = $this->get('title') ){ $admin_title = $view_title.' ‹ '.$admin_title; } return $admin_title; } /** * "admin_footer_text" filter, modifies admin footer only on Loco pages */ public function filter_admin_footer_text(){ $url = apply_filters('loco_external', 'https://localise.biz/'); return '<span id="loco-credit">'.sprintf( '<span>%s</span> <a href="%s" target="_blank">Loco</a>', esc_html(__('Loco Translate is powered by','loco')), esc_url($url) ).'</span>'; } /** * "update_footer" filter, prints Loco version number in admin footer */ public function filter_update_footer( $text ){ $html = sprintf( '<span>v%s</span>', loco_plugin_version() ); if( $this->bench ){ $info = $this->get('debug'); $html .= sprintf('<span>%sms</span>', number_format($info->time,2) ); } return $html; } /** * "loco_external" filter callback, campaignizes external links */ public function filter_loco_external( $url ){ static $query; if( ! isset($query) ){ $query = http_build_query( array( 'utm_campaign' => 'wp', 'utm_source' => 'admin', 'utm_content' => $this->get('_route') ), null, '&' ); } $u = parse_url( $url ); if( isset($u['host']) && 'localise.biz' === $u['host'] ){ $url = 'https://localise.biz'.$u['path']; if( isset($u['query']) ){ $url .= '?'. $u['query'].'&'.$query; } else { $url .= '?'.$query; } if( isset($u['fragment']) ){ $url .= '#'.$u['fragment']; } } return $url; } /** * All admin screens must define help tabs, eve if they return empty * @return array */ public function getHelpTabs(){ return array(); } /** * {@inheritdoc} */ public function get( $prop ){ return $this->view->__get($prop); } /** * {@inheritdoc} */ public function set( $prop, $value ){ $this->view->set( $prop, $value ); return $this; } /** * Render template for echoing into admin screen * @return string */ public function view( $tpl, array $args = array() ){ $view = $this->view; foreach( $args as $prop => $value ){ $view->set( $prop, $value ); } // ensure JavaScript config always present if( $jsConf = $view->js ){ if( ! $jsConf instanceof Loco_mvc_ViewParams ){ throw new InvalidArgumentException('Bad "js" view parameter'); } } else { $jsConf = new Loco_mvc_ViewParams; $view->set( 'js', $jsConf ); } // localize script if translations in memory if( is_textdomain_loaded('loco') ){ $strings = new Loco_js_Strings; $jsConf['wpl10n'] = $strings->compile(); $strings->unhook(); unset( $strings ); // add currently loaded locale for passing plural equation into js. // note that plural rules come from our data, because MO is not trusted. $tag = apply_filters( 'plugin_locale', get_locale(), 'loco' ); $jsConf['wplang'] = Loco_Locale::parse($tag); } // take benchmark for debugger to be rendered in footer if( $this->bench ){ $this->set('debug', new Loco_mvc_ViewParams( array( 'time' => microtime(true) - $this->bench, ) ) ); } return $view->render( $tpl ); } /** * Add CSS to head * @return Loco_mvc_Controller */ public function enqueueStyle( $name, array $deps = array() ){ $href = $this->baseurl.'/pub/css/'.$name.'.css'; $vers = apply_filters( 'loco_static_version', loco_plugin_version(), $href ); wp_enqueue_style( 'loco-'.strtr($name,'/','-'), $href, $deps, $vers, 'all' ); return $this; } /** * Add JavaScript to footer * @return Loco_mvc_Controller */ public function enqueueScript( $name, array $deps = array() ){ $href = $this->baseurl.'/pub/js/'.$name.'.js'; $vers = apply_filters( 'loco_static_version', loco_plugin_version(), $href ); wp_enqueue_script( 'loco-js-'.strtr($name,'/','-'), $href, $deps, $vers, true ); return $this; } } Controller.php 0000666 00000004307 15213277215 0007415 0 ustar 00 <?php /** * */ abstract class Loco_mvc_Controller extends Loco_hooks_Hookable { /** * Execute controller and return renderable output * @return string */ abstract public function render(); /** * Get view parameter * @return mixed */ abstract public function get( $prop ); /** * Set view parameter * @return Loco_mvc_Controller */ abstract public function set( $prop, $value ); /** * Default authorization check * @return Loco_mvc_Controller */ public function auth(){ if( is_user_logged_in() ){ // default capability check. child classes should override if( current_user_can('loco_admin') ){ return $this; } } $this->exitForbidden(); } /** * Emulate permission denied screen as performed in wp-admin/admin.php */ protected function exitForbidden(){ do_action( 'admin_page_access_denied' ); wp_die( __( 'You do not have sufficient permissions to access this page.','default' ), 403 ); } // @codeCoverageIgnore /** * Set a nonce for the current page for when it submits a form * @return Loco_mvc_ViewParams */ public function setNonce( $action ){ $name = 'loco-nonce'; $value = wp_create_nonce( $action ); $nonce = new Loco_mvc_ViewParams( compact('name','value','action') ); $this->set('nonce', $nonce ); return $nonce; } /** * Check if a valid nonce has been sent in current request. * Fails if nonce is invalid, but returns false if not sent so scripts can exit accordingly. * @throws Loco_error_Exception * @return bool true if data has been posted and nonce is valid */ public function checkNonce( $action ){ $posted = false; $name = 'loco-nonce'; if( isset($_REQUEST[$name]) ){ $value = $_REQUEST[$name]; if( wp_verify_nonce( $value, $action ) ){ $posted = true; } else { throw new Loco_error_Exception('Failed security check for '.$name); } } return $posted; } } AdminRouter.php 0000666 00000024332 15213277215 0007523 0 ustar 00 <?php /** * Handles execution and rendering of HTML admin pages. */ class Loco_mvc_AdminRouter extends Loco_hooks_Hookable { /** * Current admin page controller * @var Loco_mvc_AdminController */ private $ctrl; /** * admin_menu action callback */ public function on_admin_menu() { // lowest capability required to see menu items is "loco_admin" // currently also the highest (and only) capability $cap = 'loco_admin'; $user = wp_get_current_user(); $super = is_super_admin( $user->ID ); // Ensure Loco permissions are set up for the first time, or nobody will have access at all if( ! get_role('translator') || ( $super && ! is_multisite() && ! $user->has_cap($cap) ) ){ Loco_data_Permissions::init(); $user->get_role_caps(); // <- rebuild } // rendering hook for all menu items $render = array( $this, 'renderPage' ); // main loco pages, hooking only if has permission if( $user->has_cap($cap) ){ $label = __('Loco Translate','loco'); // translators: Page title for plugin home screen $title = __('Loco, Translation Management','loco'); add_menu_page( $title, $label, $cap, 'loco', $render, 'dashicons-translation' ); // alternative label for first menu item which gets repeated from top level add_submenu_page( 'loco', $title, __('Home','loco'), $cap, 'loco', $render ); $label = __('Themes','loco'); // translators: Page title for theme translations $title = __('Theme translations ‹ Loco','loco'); add_submenu_page( 'loco', $title, $label, $cap, 'loco-theme', $render ); $label = __('Plugins', 'loco'); // translators: Page title for plugin translations $title = __('Plugin translations ‹ Loco','loco'); add_submenu_page( 'loco', $title, $label, $cap, 'loco-plugin', $render ); $label = __('WordPress', 'loco'); // translators: Page title for core WordPress translations $title = __('Core translations ‹ Loco', 'loco'); add_submenu_page( 'loco', $title, $label, $cap, 'loco-core', $render ); // settings page only for users with manage_options permission in addition to Loco access: if( $user->has_cap('manage_options') ){ $title = __('Plugin settings','loco'); add_submenu_page( 'loco', $title, __('Settings','loco'), 'manage_options', 'loco-config', $render ); } // but all users need access to user preferences which require standard Loco access permission else { $title = __('User options','loco'); add_submenu_page( 'loco', $title, __('Settings','loco'), $cap, 'loco-config-user', $render ); } } // legacy link redirect from previous v1.x slug if( isset($_GET['page']) && 'loco-translate' === $_GET['page'] ){ if( wp_redirect( self::generate('') ) ){ exit(0); // <- required to avoid page permissions being checked } } } /** * Early hook as soon as we know what screen will be rendered */ public function on_current_screen( WP_Screen $screen ){ $action = isset($_GET['action']) ? $_GET['action'] : null; $this->initPage( $screen, $action ); } /** * Instantiate admin page controller from current screen. * This is called early (before renderPage) so controller can listen on other hooks. * * @return Loco_mvc_AdminController */ public function initPage( WP_Screen $screen, $action = '' ){ $class = null; $args = array (); // suppress error display when establishing Loco page $page = self::screenToPage($screen); if( is_string($page) ){ $class = self::pageToClass( $page, $action, $args ); } if( is_null($class) ){ $this->ctrl = null; return; } // class should exist, so throw fatal if it doesn't $this->ctrl = new $class; if( ! $this->ctrl instanceof Loco_mvc_AdminController ){ throw new Exception( $class.' must inherit Loco_mvc_AdminController'); } // transfer flash messages from session to admin notice buffer try { $session = Loco_data_Session::get(); while( $message = $session->flash('success') ){ Loco_error_AdminNotices::success( $message ); } } catch( Exception $e ){ Loco_error_AdminNotices::debug( $e->getMessage() ); } // buffer errors during controller setup try { $this->ctrl->_init( $_GET + $args ); do_action('loco_admin_init', $this->ctrl ); } catch( Loco_error_Exception $e ){ Loco_error_AdminNotices::add( $e ); } return $this->ctrl; } /** * Convert WordPress internal WPScreen $id into route prefix for an admin page controller * @return array */ private static function screenToPage( WP_Screen $screen ){ // Hooked menu slug is either "toplevel_page_loco" or "{title}_page_loco-{page}" // Sanitized {title} prefix is not reliable as it may be localized. instead just checking for "_page_loco" // TODO is there a safer WordPress way to resolve this? $id = $screen->id; $start = strpos($id,'_page_loco'); // not one of our pages if token not found if( is_int($start) ){ $page = substr( $id, $start+11 ) or $page = ''; return $page; } } /** * Get unvalidated controller class for given route parameters * Abstracted from initPage so we can validate routes in self::generate * @return string */ private static function pageToClass( $page, $action, array &$args ){ $routes = array ( '' => 'Root', 'debug' => 'Debug', // site-wide plugin configurations 'config' => 'config_Settings', 'config-user' => 'config_Prefs', 'config-version' => 'config_Version', // bundle type listings 'theme' => 'list_Themes', 'plugin' => 'list_Plugins', 'core' => 'list_Core', // bundle level views '{type}-view' => 'bundle_View', '{type}-conf' => 'bundle_Conf', '{type}-setup' => 'bundle_Setup', '{type}-debug' => 'bundle_Debug', // file initialization '{type}-msginit' => 'init_InitPo', '{type}-xgettext' => 'init_InitPot', // file resource views '{type}-file-view' => 'file_View', '{type}-file-edit' => 'file_Edit', '{type}-file-info' => 'file_Info', '{type}-file-delete' => 'file_Delete', ); if( ! $page ){ $page = $action; } else if( $action ){ $page .= '-'. $action; } $args['_route'] = $page; // tokenize path arguments if( preg_match('/^(plugin|theme|core)-/', $page, $r ) ){ $args['type'] = $r[1]; $page = substr_replace( $page, '{type}', 0, strlen($r[1]) ); } if( isset($routes[$page]) ){ return 'Loco_admin_'.$routes[$page].'Controller'; } // debug routing failures: // throw new Exception( sprintf('Failed to get page class from $page=%s',$page) ); } /** * Main entry point for admin menu callback, establishes page and hands off to controller */ public function renderPage(){ try { // show deferred failure from initPage if( ! $this->ctrl ){ throw new Loco_error_Exception( __('Page not found','loco') ); } // display loco admin page echo $this->ctrl->render(); } catch( Exception $e ){ $ctrl = new Loco_admin_ErrorController; $ctrl->_init( array() ); echo $ctrl->renderError($e); } // ensure session always shutdown cleanly after render Loco_data_Session::close(); } /** * Generate a routable link to Loco admin page * @return string */ public static function generate( $route, array $args = array() ){ $url = null; $page = null; $action = null; // empty action targets plugin root if( ! $route ){ $route = 'loco'; } // support direct usage of page hooks if( $url = menu_page_url( $route, false ) ){ $page = $route; } // else split action into admin page (e.g. "loco-themes") and sub-action (e.g. "view-theme") else { $page = 'loco'; $path = explode( '-', $route ); if( $sub = array_shift($path) ){ $page .= '-'.$sub; if( $path ){ $action = implode('-',$path); } } } // sanitize extended route in debug mode only. useful in tests if( loco_debugging() ){ $tmp = array(); $class = self::pageToClass( substr($page,5), $action, $tmp ); if( ! $class || ! class_exists($class) ){ throw new InvalidArgumentException( sprintf('Invalid admin route: %s => %s', json_encode($route), json_encode($class) ) ); } } // if url found, it should contain the page if( $url ){ unset( $args['page'] ); } // else start with base URL else { $url = admin_url('admin.php'); $args['page'] = $page; } // add action if found if( $action ){ $args['action'] = $action; } // else ensure not set in args, as it's reserved else { unset( $args['action'] ); } // append all arguments to base URL if( $query = http_build_query($args,null,'&') ){ $sep = false === strpos($url, '?') ? '?' : '&'; $url .= $sep.$query; } return $url; } } ViewParams.php 0000666 00000007024 15213277215 0007347 0 ustar 00 <?php /** * */ class Loco_mvc_ViewParams extends ArrayObject implements JsonSerializable { /** * Default escape function for view type is HTML * @return string */ public function escape( $text ){ return htmlspecialchars( $text, ENT_COMPAT, 'UTF-8' ); } /** * format integer as string date, including time according to user settings */ public static function date_i18n( $u, $f = null ){ static $tf, $df, $tz; if( is_null($f) ){ if( ! $tf ){ $tf = get_option('time_format') or $tf = 'g:i A'; $df = get_option('date_format') or $df= 'M jS Y'; } $f = $df.' '.$tf; } // Fix Wordpress's broken timezone implementation if( is_null($tz) ){ $tz = date_default_timezone_get() or $tz = 'UTC'; $wp = get_option('timezone_string') or $wp = $tz; if( $tz !== $wp ){ date_default_timezone_set( $wp ); } } return date_i18n( $f, $u ); } /** * @internal * @return mixed */ public function __get( $p ){ return isset($this[$p]) ? $this[$p] : null; } /** * @return bool */ public function has( $p ){ return isset($this[$p]); } /** * Print escaped property value * @param string property key * @param mixed optional arguments to substitute into value * @return void */ public function e( $p ){ $text = $this->__get($p); if( 1 < func_num_args() ){ $args = func_get_args(); $text = call_user_func_array( 'sprintf', $args ); } echo $this->escape( $text ); return ''; } /** * Print property as string date, including time */ public function date( $p, $f = null ){ if( $u = $this->__get($p) ){ $s = self::date_i18n( $u, $f ); } else { $s = ''; } echo $this->escape($s); return ''; } /** * Print property as a string-formatted number */ public function n( $p, $dp = null ){ // number_format_i18n is pre-escaped for HTML echo number_format_i18n( $this->__get($p), $dp ); return ''; } /** * Format property with passed formatting string */ public function f( $p, $f = '%s' ){ echo $this->escape( sprintf( $f, $this->__get($p) ) ); return ''; } /** * @return array */ public function jsonSerialize(){ return $this->getArrayCopy(); } /** * Fetch whole object as JSON * @return string */ public function exportJson(){ return json_encode( $this->jsonSerialize() ); } /** * @return Loco_mvc_ViewParams */ public function concat( ArrayObject $more ){ foreach( $more as $name => $value ){ $this[$name] = $value; } return $this; } /** * Debugging function * @codeCoverageIgnore */ public function dump(){ echo '<pre>',$this->escape( json_encode( $this->getArrayCopy(),JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE ) ),'</pre>'; } // The following are all aliases for WordPress output functions in formatting.php /*public function html( $p ){ return esc_html( $this->__get($p) ); }*/ /*public function attr( $p ){ return esc_attr( $this->__get($p) ); }*/ } FileParams.php 0000666 00000011541 15213277215 0007313 0 ustar 00 <?php /** * Abstracts information about a file into a view parameter object. */ class Loco_mvc_FileParams extends Loco_mvc_ViewParams { /** * File reference from which to take live properties. * @var Loco_fs_File */ private $file; /** * Print property as a number of bytes in larger denominations */ public static function renderBytes( $n ){ $i = 0; $dp = 0; while( $n >= 1024 ){ $i++; $dp++; $n /= 1024; } $s = number_format( $n, $dp, '.', ',' ); // trim trailing zeros from decimal places $a = explode('.',$s); if( isset($a[1]) ){ $s = $a[0]; $d = trim($a[1],'0') and $s .= '.'.$d; } $units = array( ' bytes', ' KB', ' MB', ' GB', ' TB' ); $s .= $units[$i]; return $s; } /** * @return Loco_mvc_FileParams */ public static function create( Loco_fs_File $file ) { return new Loco_mvc_FileParams( array(), $file ); } /** * Override does lazy property initialization */ public function __construct( array $props = array(), Loco_fs_File $file ){ parent::__construct( array ( 'name' => '', 'path' => '', 'relpath' => '', 'reltime' => '', 'bytes' => 0, 'size' => '', 'imode' => '', 'smode' => '', 'owner' => '', 'group' => '', ) + $props ); $this->file = $file; } /** * {@inheritdoc} * Override to get live information from file object */ public function offsetGet( $prop ){ $getter = array( $this, '_get_'.$prop ); if( is_callable($getter) ){ return call_user_func( $getter ); } return parent::offsetGet($prop); } /** * {@inheritdoc} * Override to ensure all properties populated */ public function getArrayCopy(){ $a = array(); foreach( $this as $prop => $dflt ){ $a[$prop] = $this[$prop]; } return $a; } /** * @return string */ private function _get_name(){ return $this->file->basename(); } /** * @return string */ private function _get_path(){ return $this->file->getPath(); } /** * @return string */ private function _get_relpath(){ $base = loco_constant('WP_CONTENT_DIR'); return $this->file->getRelativePath($base); } /** * Using slightly modified version of WordPress's Human time differencing * + Added "Just now" when in the last 30 seconds * TODO possibly replace with custom function that includes "Yesterday" etc.. */ private function _get_reltime(){ $time = $this->file->modified(); $time_diff = time() - $time; // use same time format as posts listing when in future or more than a day ago if( $time_diff < 0 || $time_diff >= 86400 ){ return date_i18n( __('Y/m/d','default'), $time ); } if( $time_diff < 30 ){ // translators: relative time when something happened in the last 30 seconds return __('Just now','loco'); } return sprintf( __('%s ago','default'), human_time_diff($time) ); } /** * @return int */ private function _get_bytes(){ return $this->file->size(); } /** * @return string */ private function _get_size(){ return self::renderBytes( $this->_get_bytes() ); } /** * Get octal file mode * @return string */ private function _get_imode(){ $mode = new Loco_fs_FileMode( $this->file->mode() ); return (string) $mode; } /** * Get rwx file mode * @return string */ private function _get_smode(){ $mode = new Loco_fs_FileMode( $this->file->mode() ); return $mode->format(); } /** * Get file owner name * @return string */ private function _get_owner(){ if( ( $uid = $this->file->uid() ) && function_exists('posix_getpwuid') && ( $a = posix_getpwuid($uid) ) ){ return $a['name']; } return sprintf('%u',$uid); } /** * Get group owner name * @return string */ private function _get_group(){ if( ( $gid = $this->file->gid() ) && function_exists('posix_getpwuid') && ( $a = posix_getgrgid($gid) ) ){ return $a['name']; } return sprintf('%u',$gid); } /** * Print pseudo console line */ public function ls(){ $this->e('smode'); echo ' '; $this->e('owner'); echo ':'; $this->e('group'); echo ' '; $this->e('relpath'); return ''; } } View.php 0000666 00000015540 15213277215 0006205 0 ustar 00 <?php /** * View renderer */ class Loco_mvc_View implements IteratorAggregate { /** * @var Loco_mvc_ViewParams */ private $scope; /** * View that is decorating current view * @var Loco_mvc_View */ private $parent; /** * Current template as full path to PHP file * @var string */ private $template; /** * Current working directory for finding templates by relative path * @var string */ private $cwd; /** * Name of current output buffer * @var string */ private $block; /** * @internal */ public function __construct( array $args = array() ){ $this->scope = new Loco_mvc_ViewParams( $args ); $this->cwd = loco_plugin_root().'/tpl'; } /** * Change base path for template paths * @param string path relative to current directory * @return Loco_mvc_View */ public function cd( $path ){ if( $path && '/' === $path{0} ){ $this->cwd = rtrim( loco_plugin_root().'/tpl'.$path, '/' ); } else { $this->cwd = rtrim( $this->cwd.'/'.$path ); } return $this; } /** * @internal * Clean up if something ubruptly stopped rendering before graceful end */ public function __destruct(){ if( $this->block ){ ob_end_clean(); } } /** * Render error screen HTML * @return string */ public static function renderError( Loco_error_Exception $e ){ $view = new Loco_mvc_View; try { $view->set( 'error', $e ); return $view->render( $e->getTemplate() ); } catch( Exception $e ){ return '<h1>'.esc_html( $e->getMessage() ).'</h1>'; } } /** * @internal * Make this view a child of another template. i.e. decorate this with that. * Parent will have access to original argument scope, but separate from now on * @return Loco_mvc_View the parent view */ private function extend( $tpl ){ $this->parent = new Loco_mvc_View; $this->parent->cwd = $this->cwd; $this->parent->setTemplate( $tpl ); return $this->parent; } /** * @internal * After start is called any captured output will be placed in the named variable * @return void */ private function start( $name ){ $this->stop(); $this->scope[$name] = null; $this->block = $name; } /** * @internal * When stop is called, buffered output is saved into current variable for output by parent template, or at end of script. * @return void */ private function stop(){ $content = ob_get_contents(); ob_clean(); if( $b = $this->block ){ if( isset($this->scope[$b]) ){ $content = $this->scope[$b].$content; } $this->scope[$b] = new _LocoViewBuffer($content); $this->block = null; } $this->block = '_trash'; } /** * implement IteratorAggregate::getIterator */ public function getIterator(){ return $this->scope; } /** * @return mixed */ public function __get( $prop ){ return isset($this->scope[$prop]) ? $this->scope[$prop] : null; } /** * @return bool */ public function has( $prop ){ return isset( $this->scope[$prop] ); } /** * Set a view argument * @return Loco_mvc_View */ public function set( $prop, $value ){ $this->scope[$prop] = $value; return $this; } /** * Main entry to rendering complete template * @param string template name excluding extension * @param array extra arguments to set in view scope * @return string */ public function render( $tpl, array $args = null, Loco_mvc_View $parent = null ){ if( $this->block ){ return $this->fork()->render( $tpl, $args, $this ); } $this->setTemplate($tpl); if( $parent && $this->template === $parent->template ){ throw new Loco_error_Exception('Avoiding infinite loop'); } if( is_array($args) ){ foreach( $args as $prop => $value ){ $this->set($prop, $value); } } ob_start(); $content = $this->buffer(); ob_end_clean(); return $content; } /** * Do actual render of currently validated template path * @return string content not captured in sub-blocks */ private function buffer(){ $this->start('_trash'); $this->execTemplate( $this->template ); $this->stop(); $this->block = null; // decorate via parent view if there is one if( $this->parent ){ $this->parent->scope = clone $this->scope; $this->parent->set('_content', $this->_trash ); return $this->parent->buffer(); } // else at the root of view chain return (string) $this->_trash; } /** * Set current template * @param string path tro template, excluding file extension */ public function setTemplate( $tpl ){ $file = new Loco_fs_File( $tpl.'.php' ); $file->normalize( $this->cwd ); if( ! $file->exists() ){ $debug = str_replace( loco_plugin_root().'/', '', $file->getPath() ); throw new Loco_error_Exception( 'Template not found: '.$debug ); } $this->cwd = $file->dirname(); $this->template = $file->getPath(); } /** * @return Loco_mvc_View */ private function fork(){ $view = new Loco_mvc_View; $view->cwd = $this->cwd; $view->scope = clone $this->scope; return $view; } /** * Do actual runtime template include */ private function execTemplate( $template ){ $params = $this->scope; extract( $params->getArrayCopy() ); include $template; } /** * Link generator * return Loco_mvc_ViewParams */ public function route( $action, array $args = array() ){ return new Loco_mvc_ViewParams( array( 'href' => Loco_mvc_AdminRouter::generate( $action, $args ), ) ); } /** * Shorthand for `echo esc_html( sprintf( ...` * @return void */ private function e( $text ){ if( 1 < func_num_args() ){ $args = func_get_args(); $text = call_user_func_array( 'sprintf', $args ); } echo htmlspecialchars( $text, ENT_COMPAT, 'UTF-8' ); return ''; } } /** * @internal */ class _LocoViewBuffer { private $s; public function __construct( $s ){ $this->s = $s; } public function __toString(){ return $this->s; } } HiddenFields.php 0000666 00000001554 15213277215 0007615 0 ustar 00 <?php /** * */ class Loco_mvc_HiddenFields extends Loco_mvc_ViewParams { /** * @internal * Echo all hidden fields to output buffer */ public function _e(){ foreach( $this as $name => $value ){ echo '<input type="hidden" name="',$this->escape($name),'" value="',$this->escape($value),'" />'; } } /** * Add a nonce field * @return Loco_mvc_HiddenFields */ public function setNonce( $action ){ $this['loco-nonce'] = wp_create_nonce( $action ); return $this; } /** * Load postdata fields * @return Loco_mvc_HiddenFields */ public function addPost( Loco_mvc_PostParams $post ){ foreach( $post->getSerial() as $pair ){ $this[ $pair[0] ] = isset($pair[1]) ? $pair[1] : ''; } return $this; } } AjaxController.php 0000666 00000004114 15213277215 0010215 0 ustar 00 <?php /** * */ abstract class Loco_mvc_AjaxController extends Loco_mvc_Controller { /** * Request arguments injected from Router * @var ArrayObject */ private $input; /** * Data to respond with as JSON * @var ArrayObject */ private $output; /** * Pre-init call invoked by router * @return Loco_mvc_AjaxController */ final public function _init( array $args ){ $this->auth(); $this->output = new ArrayObject; $this->input = new ArrayObject( $args ); // avoid fatal error if json extension is missing loco_check_extension('json'); } /** * Get posted data and validate nonce in the process * @return Loco_mvc_PostParams */ protected function validate(){ $route = $this->input['route']; if( ! $this->checkNonce($route) ){ throw new Loco_error_Exception( sprintf('Ajax %s action requires postdata with nonce',$route) ); } return Loco_mvc_PostParams::get(); } /** * {@inheritdoc} */ public function get( $prop ){ return isset($this->input[$prop]) ? $this->input[$prop] : null; } /** * {@inheritdoc} */ public function set( $prop, $value ){ $this->output[$prop] = $value; return $this; } /** * @return string JSON */ public function render(){ $data = array ( 'data' => $this->output->getArrayCopy(), ); // non-fatal notices deliberately not in "error" key if( $array = Loco_error_AdminNotices::destroyAjax() ){ $data['notices'] = $array; } return json_encode( $data ); } /** * Pretty json encode if PHP version allows * protected function json_encode( $data ){ $opts = 0; if( defined('JSON_PRETTY_PRINT') ){ $opts |= JSON_PRETTY_PRINT; } if( defined('JSON_UNESCAPED_SLASHES') ){ $opts |= JSON_UNESCAPED_SLASHES; } return json_encode( $data, $opts ); }*/ }
| ver. 1.4 |
Github
|
.
| PHP 7.0.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings