Commit 747823ea authored by Simon's avatar Simon

Search focus & Header scroll hide

parent 2c1f3c56
......@@ -4,7 +4,7 @@ Plugin Name: Loco Translate
Plugin URI: https://wordpress.org/plugins/loco-translate/
Description: Translate themes and plugins directly in WordPress
Author: Tim Whitlock
Version: 2.4.3
Version: 2.4.4
Author URI: https://localise.biz/wordpress/plugin
Text Domain: loco-translate
Domain Path: /languages/
......@@ -30,7 +30,7 @@ function loco_plugin_file(){
* @return string
*/
function loco_plugin_version(){
return '2.4.3';
return '2.4.4';
}
......@@ -98,7 +98,18 @@ function loco_constant( $name ){
function loco_include( $relpath ){
$path = loco_plugin_root().'/'.$relpath;
if( ! file_exists($path) ){
throw new Loco_error_Exception('File not found: '.$path);
$message = 'File not found: '.$path;
// debug specifics to error log in case full call stack not visible
if( 'cli' !== PHP_SAPI ) {
error_log( sprintf( '[Loco.debug] Failed on loco_include(%s). !file_exists(%s)', var_export($relpath,true), var_export($path,true) ), 0 );
}
// handle circular file inclusion error if error class not found
if( loco_class_exists('Loco_error_Exception') ){
throw new Loco_error_Exception($message);
}
else {
throw new Exception($message.'; additionally src/error/Exception.php not loadable');
}
}
return include $path;
}
......@@ -137,7 +148,7 @@ function loco_check_extension( $name ) {
/**
* Class autoloader for Loco classes under src directory.
* e.g. class "Loco_foo_FooBar" wil be found in "src/foo/FooBar.php"
* e.g. class "Loco_foo_Bar" wil be found in "src/foo/Bar.php"
* Also does autoload for polyfills under "src/compat" if $name < 20 chars
*
* @internal
......@@ -156,22 +167,44 @@ function loco_autoload( $name ){
}
}
spl_autoload_register( 'loco_autoload', false );
// provide safe directory for custom translations that won't be deleted during auto-updates
if( ! defined('LOCO_LANG_DIR') ){
define( 'LOCO_LANG_DIR', trailingslashit(loco_constant('WP_LANG_DIR')).'loco' );
/**
* class_exists wrapper that fails silently.
* @param string class name
* @return bool
*/
function loco_class_exists( $class ){
try {
return class_exists($class,true);
}
catch( Exception $e ){
return false;
}
}
// text domain loading helper for custom file locations. disable by setting constant empty
if( LOCO_LANG_DIR ){
new Loco_hooks_LoadHelper;
}
// Startup errors will raise notices. Check your error logs if error reporting is quiet
try {
spl_autoload_register( 'loco_autoload', false );
// provide safe directory for custom translations that won't be deleted during auto-updates
if ( ! defined( 'LOCO_LANG_DIR' ) ) {
define( 'LOCO_LANG_DIR', trailingslashit( loco_constant('WP_LANG_DIR') ) . 'loco' );
}
// text domain loading helper for custom file locations. Set constant empty to disable
if ( LOCO_LANG_DIR ) {
new Loco_hooks_LoadHelper;
}
// initialize hooks for admin screens
if( is_admin() ){
new Loco_hooks_AdminHooks;
// initialize hooks for admin screens
if ( is_admin() ) {
new Loco_hooks_AdminHooks;
}
}
catch( Exception $e ){ // PHP5+
trigger_error(sprintf('[Loco.fatal] %s in %s:%u',$e->getMessage(), $e->getFile(), $e->getLine() ),E_USER_NOTICE);
}
catch( Throwable $e ){ // PHP7+
trigger_error(sprintf('[Loco.fatal] %s in %s:%u',$e->getMessage(), $e->getFile(), $e->getLine() ),E_USER_NOTICE);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
#loco-admin.wrap .notice-info dl{margin-top:0;display:inline-block}#loco-admin.wrap .notice-info dl dt,#loco-admin.wrap .notice-info dl dd{line-height:1.4em}#loco-admin.wrap .notice-info dl dt{font-weight:bold;color:#555}#loco-admin.wrap .notice-info dl dd{margin-left:0;margin-bottom:.8em}#loco-admin.wrap .notice-info dl div.progress .l{display:none}
\ No newline at end of file
#loco-admin.wrap .notice-info nav{display:block;position:absolute;right:0;top:0;font-size:1.3em;padding:1em}#loco-admin.wrap .notice-info nav a{color:#666}#loco-admin.wrap .notice-info nav a:hover{color:#000;text-decoration:none}#loco-admin.wrap .notice-info dl{margin-top:0;display:inline-block}#loco-admin.wrap .notice-info dl dt,#loco-admin.wrap .notice-info dl dd{line-height:1.4em}#loco-admin.wrap .notice-info dl dt{font-weight:bold;color:#555}#loco-admin.wrap .notice-info dl dd{margin-left:0;margin-bottom:.8em}#loco-admin.wrap .notice-info dl div.progress .l{display:none}
\ No newline at end of file
#loco-admin.wrap .revisions-diff{padding:10px;min-height:20px}#loco-admin.wrap table.diff{border-collapse:collapse}#loco-admin.wrap table.diff td{white-space:nowrap;overflow:hidden;font:normal 12px/17px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace;padding:2px}#loco-admin.wrap table.diff td>span{color:#aaa}#loco-admin.wrap table.diff td>span:after{content:". "}#loco-admin.wrap table.diff tbody{border-top:1px dashed #ccc}#loco-admin.wrap table.diff tbody:first-child{border-top:none}#loco-admin.wrap .revisions.loading .diff-meta{color:#eee}#loco-admin.wrap .revisions.loading .loading-indicator span.spinner{visibility:visible;background:#fff url(../img/spin-modal.gif?v=2.4.1) center center no-repeat}#loco-admin.wrap .revisions-meta{clear:both;padding:10px 12px;margin:0;position:relative;top:10px}#loco-admin.wrap .revisions-meta .diff-meta{clear:none;float:left;width:50%;padding:0;min-height:auto}#loco-admin.wrap .revisions-meta .diff-meta button{margin-top:5px}#loco-admin.wrap .revisions-meta .diff-meta-current{float:right;text-align:right}#loco-admin.wrap .revisions-meta time{color:#72777c}#loco-admin.wrap .revisions-control-frame{margin:10px 0}#loco-admin.wrap .revisions-diff-frame{margin-top:20px}
\ No newline at end of file
#loco-admin.wrap .revisions-diff{padding:10px;min-height:20px}#loco-admin.wrap table.diff{border-collapse:collapse}#loco-admin.wrap table.diff td{white-space:nowrap;overflow:hidden;font:normal 12px/17px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace;padding:2px}#loco-admin.wrap table.diff td>span{color:#aaa}#loco-admin.wrap table.diff td>span:after{content:". "}#loco-admin.wrap table.diff tbody{border-top:1px dashed #ccc}#loco-admin.wrap table.diff tbody:first-child{border-top:none}#loco-admin.wrap .revisions.loading .diff-meta{color:#eee}#loco-admin.wrap .revisions.loading .loading-indicator span.spinner{visibility:visible;background:#fff url(../img/spin-modal.gif?v=2.4.4) center center no-repeat}#loco-admin.wrap .revisions-meta{clear:both;padding:10px 12px;margin:0;position:relative;top:10px}#loco-admin.wrap .revisions-meta .diff-meta{clear:none;float:left;width:50%;padding:0;min-height:auto}#loco-admin.wrap .revisions-meta .diff-meta button{margin-top:5px}#loco-admin.wrap .revisions-meta .diff-meta-current{float:right;text-align:right}#loco-admin.wrap .revisions-meta time{color:#72777c}#loco-admin.wrap .revisions-control-frame{margin:10px 0}#loco-admin.wrap .revisions-diff-frame{margin-top:20px}
\ No newline at end of file
.js #loco-admin.wrap .loco-loading{min-height:100px;background:#fff url(../img/spin-modal.gif?v=2.4.1) center center no-repeat}.js #loco-admin.wrap .loco-loading ol.msgcat{display:none}#loco-admin.wrap #loco-po{padding-right:0;overflow:auto}#loco-admin.wrap ol.msgcat{margin-left:3em;padding-top:1em;border-top:1px dashed #ccc}#loco-admin.wrap ol.msgcat:first-child{padding-top:0;border-top:none}#loco-admin.wrap ol.msgcat li{color:#aaa;margin:0;padding:0 0 0 1em;font:normal 12px/17px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace;border-left:1px solid #eee}#loco-admin.wrap ol.msgcat li>*{color:#333;white-space:pre}#loco-admin.wrap ol.msgcat li>.po-comment{color:#3cc200}#loco-admin.wrap ol.msgcat li>.po-refs{color:#0073aa}#loco-admin.wrap ol.msgcat li>.po-refs a{color:inherit;text-decoration:none}#loco-admin.wrap ol.msgcat li>.po-refs a:hover{text-decoration:underline}#loco-admin.wrap ol.msgcat li>.po-flags{color:#77904a}#loco-admin.wrap ol.msgcat li>.po-flags em{font-style:normal}#loco-admin.wrap ol.msgcat li>.po-word{color:#000}#loco-admin.wrap ol.msgcat li>.po-junk{font-style:italic;color:#ccc}#loco-admin.wrap ol.msgcat li>.po-string>span{color:#c931c7}#loco-admin.wrap form.loco-filter{top:0;right:0;position:absolute}#loco-admin.wrap .loco-invalid form.loco-filter input[type=text]:focus{border-color:#c00;-webkit-box-shadow:0 0 2px rgba(153,0,0,.5);-moz-box-shadow:0 0 2px rgba(153,0,0,.5);box-shadow:0 0 2px rgba(153,0,0,.5)}#loco-admin.wrap .loco-invalid ol.msgcat{list-style-type:none}#loco-admin.wrap .loco-invalid ol.msgcat li{color:#000}
\ No newline at end of file
.js #loco-admin.wrap .loco-loading{min-height:100px;background:#fff url(../img/spin-modal.gif?v=2.4.4) center center no-repeat}.js #loco-admin.wrap .loco-loading ol.msgcat{display:none}#loco-admin.wrap #loco-po{padding-right:0;overflow:auto}#loco-admin.wrap ol.msgcat{margin-left:3em;padding-top:1em;border-top:1px dashed #ccc}#loco-admin.wrap ol.msgcat:first-child{padding-top:0;border-top:none}#loco-admin.wrap ol.msgcat li{color:#aaa;margin:0;padding:0 0 0 1em;font:normal 12px/17px "Monaco","Menlo","Ubuntu Mono","Consolas","source-code-pro",monospace;border-left:1px solid #eee}#loco-admin.wrap ol.msgcat li>*{color:#333;white-space:pre}#loco-admin.wrap ol.msgcat li>.po-comment{color:#3cc200}#loco-admin.wrap ol.msgcat li>.po-refs{color:#0073aa}#loco-admin.wrap ol.msgcat li>.po-refs a{color:inherit;text-decoration:none}#loco-admin.wrap ol.msgcat li>.po-refs a:hover{text-decoration:underline}#loco-admin.wrap ol.msgcat li>.po-flags{color:#77904a}#loco-admin.wrap ol.msgcat li>.po-flags em{font-style:normal}#loco-admin.wrap ol.msgcat li>.po-word{color:#000}#loco-admin.wrap ol.msgcat li>.po-junk{font-style:italic;color:#ccc}#loco-admin.wrap ol.msgcat li>.po-string>span{color:#c931c7}#loco-admin.wrap form.loco-filter{top:0;right:0;position:absolute}#loco-admin.wrap .loco-invalid form.loco-filter input[type=text]:focus{border-color:#c00;-webkit-box-shadow:0 0 2px rgba(153,0,0,.5);-moz-box-shadow:0 0 2px rgba(153,0,0,.5);box-shadow:0 0 2px rgba(153,0,0,.5)}#loco-admin.wrap .loco-invalid ol.msgcat{list-style-type:none}#loco-admin.wrap .loco-invalid ol.msgcat li{color:#000}
\ No newline at end of file
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(9,100,132,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#096484;foo:#096484}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1cdc7 !important;background:#db9925 !important;border-color:#db9925 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/blue/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(9,100,132,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#096484;foo:#096484}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1cdc7 !important;background:#db9925 !important;border-color:#db9925 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/blue/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(199,165,137,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#c7a589;foo:#c7a589}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1ccc7 !important;background:#ba906d !important;border-color:#ba906d !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/coffee/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(199,165,137,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#c7a589;foo:#c7a589}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1ccc7 !important;background:#ba906d !important;border-color:#ba906d !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/coffee/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(163,183,69,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#a3b745;foo:#a3b745}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#cfd1c7 !important;background:#89993a !important;border-color:#89993a !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/ectoplasm/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(163,183,69,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#a3b745;foo:#a3b745}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#cfd1c7 !important;background:#89993a !important;border-color:#89993a !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/ectoplasm/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(136,136,136,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#888;foo:#04a4cc}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7cfd1 !important;background:#0384a4 !important;border-color:#0384a4 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/light/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(136,136,136,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#888;foo:#04a4cc}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7cfd1 !important;background:#0384a4 !important;border-color:#0384a4 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/light/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(225,77,67,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#e14d43;foo:#e14d43}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1c8c7 !important;background:#d92e23 !important;border-color:#d92e23 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/midnight/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(225,77,67,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#e14d43;foo:#e14d43}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1c8c7 !important;background:#d92e23 !important;border-color:#d92e23 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/midnight/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(56,88,233,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#3858e9;foo:#3858e9}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7c9d1 !important;background:#193ddf !important;border-color:#193ddf !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/modern/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(56,88,233,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#3858e9;foo:#3858e9}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7c9d1 !important;background:#193ddf !important;border-color:#193ddf !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/modern/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(158,186,160,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#9ebaa0;foo:#9ebaa0}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7d1c8 !important;background:#86a989 !important;border-color:#86a989 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/ocean/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(158,186,160,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#9ebaa0;foo:#9ebaa0}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#c7d1c8 !important;background:#86a989 !important;border-color:#86a989 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/ocean/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(221,130,59,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#dd823b;foo:#dd823b}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1cbc7 !important;background:#cc6d23 !important;border-color:#cc6d23 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/sunrise/spin-primary-button.gif?v=2.4.1) 0 0 no-repeat !important}
.wrap #loco-editor .is-table .wg-tr:nth-child(even){background-color:rgba(221,130,59,.05)}.wrap #loco-editor .wg-split-x>nav.wg-tabs>a.active,.wrap #loco-editor .is-table .wg-cols>div>div.selected{background-color:#dd823b;foo:#dd823b}.wrap #loco-editor .is-editable>.wg-content>textarea:focus,.wrap #loco-editor .is-editable>.wg-content.has-focus .ace_scroller,.wrap #loco-editor .is-editable>.wg-content.has-focus .mce-content-body{border-color:#5b9dd9;-webkit-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);-moz-box-shadow:inset 0 0 .6em rgba(30,140,190,.8);box-shadow:inset 0 0 .6em rgba(30,140,190,.8)}.wp-core-ui .button-primary.loco-loading[disabled]{color:#d1cbc7 !important;background:#cc6d23 !important;border-color:#cc6d23 !important}.wp-core-ui .button-primary.loco-loading[disabled]:before{background:transparent url(../../img/skins/sunrise/spin-primary-button.gif?v=2.4.4) 0 0 no-repeat !important}
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(t,e,o){var c,u,i=e.getElementById("loco-fs"),a=e.getElementById("loco-move"),l=a.path.value;function d(n){o(a).find("button.button-primary").each(function(t,e){e.disabled=n})}i&&a&&(c=t.loco.fs.init(i).setForm(a).listen(function(t){d(!(t&&u))}),o(a).change(function(t){var e,n=t.target||{};"dest"!==n.name||!n.checked&&"text"!==n.type||(e=n.value)&&e!==u&&(u=e,d(!0),l!==e&&(i.dest.value=e,c.connect()))}).submit(function(t){return!!u||(t.preventDefault(),!1)}))}(window,document,jQuery);
\ No newline at end of file
!function(t,e,o){var c,i,u=e.getElementById("loco-fs"),a=e.getElementById("loco-main"),d=a.path.value;function l(n){o(a).find("button.button-primary").each(function(t,e){e.disabled=n})}u&&a&&(c=t.loco.fs.init(u).setForm(a).listen(function(t){l(!(t&&i))}),o(a).change(function(t){var e,n=t.target||{};"dest"!==n.name||!n.checked&&"text"!==n.type||(e=n.value)&&e!==i&&(i=e,l(!0),d!==e&&(u.dest.value=e,c.connect()))}).submit(function(t){return!!i||(t.preventDefault(),!1)}))}(window,document,window.jQuery);
\ No newline at end of file
!function(t,n,c){var i,e,a,l,o,r,u,s=t.loco,f=n.getElementById("loco-fs"),d=n.getElementById("loco-poinit"),v=f&&s.fs.init(f),g=(a=(e=d)["select-locale"],l=e["custom-locale"],o=e["use-selector"],r=c(a).focus(p).closest("fieldset").click(p)[0],u=c(l).focus(x).closest("fieldset").click(x)[0],c(o).change(m),m(),s.watchtext(l,function(t){c(l.form).triggerHandler("change")}),{val:function(){var t=b();return t?s.locale.parse(t):s.locale.clone({lang:"zxx"})}});function h(){return o[0].checked}function p(){y(o[0].checked=!0)}function x(){l.value||(l.value=b()),y(!(o[1].checked=!0))}function b(){var t=c(h()?a:l).serializeArray();return t[0]&&t[0].value||""}function m(){return y(h()),!0}function y(t){l.disabled=t,a.disabled=!t,u.className=t?"disabled":"active",r.className=t?"active":"disabled",I()}var z,A=(z=d["select-path"],{val:function(){var t=k("path");return t&&t.value},txt:function(){var t=k("path");return t&&c(t.parentNode).find("code.path").text()}});function k(t){var n=function(){var t=c(z).serializeArray()[0];return t&&t.value||null}();return n&&d[t+"["+n+"]"]}function w(e){c(d).find("button.button-primary").each(function(t,n){n.disabled=e})}function I(){var t=g&&g.val(),n=t&&t.isValid()&&"zxx"!==t.lang,e=A&&A.val(),a=n&&e;if(j(t),w(!0),a){var c=A.txt();c!==i?(i=c,f.path.value=i,v.listen(N).connect()):w(!1)}}function N(t){w(!t)}function j(e){var t=c(d),n=e&&e.toString("_")||"",a=n?"zxx"===n?"<locale>":n:"<invalid>";t.find("code.path span").each(function(t,n){n.textContent=a}),t.find("span.lang").each(function(t,n){!function(t,n){n&&"zxx"!==n.lang?(t.setAttribute("lang",n.lang),t.setAttribute("class",n.getIcon())):(t.setAttribute("lang",""),t.setAttribute("class","lang nolang"))}(n,e)})}function B(t){var n=t&&t.redirect;n&&location.assign(n)}c(d).change(I).submit(function(t){return t.preventDefault(),v.applyCreds(d),s.ajax.submit(t.target,B),!1}),j(g.val())}(window,document,jQuery);
\ No newline at end of file
!function(t,n,c){var i,e,a,l,o,r,u,s=t.loco,f=n.getElementById("loco-fs"),d=n.getElementById("loco-poinit"),v=f&&s.fs.init(f),g=(a=(e=d)["select-locale"],l=e["custom-locale"],o=e["use-selector"],r=c(a).focus(p).closest("fieldset").click(p)[0],u=c(l).focus(x).closest("fieldset").click(x)[0],c(o).change(m),m(),s.watchtext(l,function(t){c(l.form).triggerHandler("change")}),{val:function(){var t=b();return t?s.locale.parse(t):s.locale.clone({lang:"zxx"})}});function h(){return o[0].checked}function p(){y(o[0].checked=!0)}function x(){l.value||(l.value=b()),y(!(o[1].checked=!0))}function b(){var t=c(h()?a:l).serializeArray();return t[0]&&t[0].value||""}function m(){return y(h()),!0}function y(t){l.disabled=t,a.disabled=!t,u.className=t?"disabled":"active",r.className=t?"active":"disabled",I()}var z,A=(z=d["select-path"],{val:function(){var t=k("path");return t&&t.value},txt:function(){var t=k("path");return t&&c(t.parentNode).find("code.path").text()}});function k(t){var n=function(){var t=c(z).serializeArray()[0];return t&&t.value||null}();return n&&d[t+"["+n+"]"]}function w(e){c(d).find("button.button-primary").each(function(t,n){n.disabled=e})}function I(){var t=g&&g.val(),n=t&&t.isValid()&&"zxx"!==t.lang,e=A&&A.val(),a=n&&e;if(j(t),w(!0),a){var c=A.txt();c!==i?(i=c,f.path.value=i,v.listen(N).connect()):w(!1)}}function N(t){w(!t)}function j(e){var t=c(d),n=e&&e.toString("_")||"",a=n?"zxx"===n?"{locale}":n:"{invalid}";t.find("code.path span").each(function(t,n){n.textContent=a}),t.find("span.lang").each(function(t,n){!function(t,n){n&&"zxx"!==n.lang?(t.setAttribute("lang",n.lang),t.setAttribute("class",n.getIcon())):(t.setAttribute("lang",""),t.setAttribute("class","lang nolang"))}(n,e)})}function B(t){var n=t&&t.redirect;n&&location.assign(n)}c(d).change(I).submit(function(t){return t.preventDefault(),v.applyCreds(d),s.ajax.submit(t.target,B),!1}),j(g.val())}(window,document,jQuery);
\ No newline at end of file
!function(o,n,t){var e,i,r,a=o.loco,u=(a&&a.conf||{}).multipart&&o.FormData&&o.Blob,c=n.getElementById("loco-fs"),f=n.getElementById("loco-main");function l(e){var n=t(f).find("button.button-primary");return n.each(function(n,t){t.disabled=e}),n}function d(){l(!0).addClass("loco-loading")}function s(n){l(n).removeClass("loco-loading")}function m(){f.path.value=i+"/"+r,d(),e.connect()}function v(){return i&&r&&e.authed()}function p(n,t,e){n.redirect?(s(!0),o.location.assign(n.redirect)):s(!1)}function g(){s(!1)}c&&f&&(e=o.loco.fs.init(c).setForm(f).listen(function(n){s(!(n&&i&&r))}),t(f).change(function(n){r=String(f.f.value).split(/[\\\/]/).pop();var t,e=n.target||{};if("dir"===e.name&&e.checked){if((t=e.value)&&t!==i&&(i=t,r))return void m()}else if("f"===e.name&&i)return void m();l(!v())}).submit(function(n){if(v()){if(u){n.preventDefault();var t=new FormData(f);return d(),a.ajax.post("upload",t,p,g),!1}return!0}return n.preventDefault(),!1}))}(window,document,window.jQuery);
\ No newline at end of file
......@@ -3,8 +3,8 @@ Contributors: timwhitlock
Tags: translation, translators, localization, localisation, l10n, i18n, Gettext, PO, MO, productivity, multilingual, internationalization
Requires at least: 4.1
Requires PHP: 5.2.4
Tested up to: 5.5
Stable tag: 2.4.3
Tested up to: 5.5.1
Stable tag: 2.4.4
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
......@@ -100,6 +100,16 @@ We don't collect your data or snoop on you. See the [plugin privacy notice](http
== Changelog ==
= 2.4.4 =
* Added PO file upload feature
* Added download button to file info page
* Fix for extracting plurals also used as singulars
* Updating API keys no longer require editor page reload
* Catching fatal startup errors in loco.php
* Supporting max_php_size=0 to mean no size restriction
* Auto-update detection now checks new site options
* Bumped WordPress version to 5.5.1
= 2.4.3 =
* Improved fix for default syncing of msgstr fields
* Reverted accidental removal of js debug flag
......@@ -367,7 +377,7 @@ We don't collect your data or snoop on you. See the [plugin privacy notice](http
== Upgrade Notice ==
= 2.4.3 =
= 2.4.4 =
* Various improvements and bugfixes
......
......@@ -96,6 +96,13 @@ class Loco_admin_bundle_ViewController extends Loco_admin_bundle_BaseController
'name' => __('New language','loco-translate'),
'icon' => 'add',
) );
// offer PO file upload
$p['nav'][] = new Loco_mvc_ViewParams( array(
'href' => $this->getProjectLink('upload', $project ),
'name' => __('Upload PO','loco-translate'),
'icon' => 'upload',
) );
$pot = $project->getPot();
......
......@@ -73,13 +73,14 @@ class Loco_admin_config_DebugController extends Loco_admin_config_BaseController
}
// utf8 / encoding:
$cs = get_option('blog_charset');
$encoding = new Loco_mvc_ViewParams( array (
'OK' => "\xCE\x9F\xCE\x9A",
'tick' => "\xE2\x9C\x93",
'json' => json_decode('"\\u039f\\u039a \\u2713"'),
'charset' => $cs.' '.( preg_match('/^utf-?8$/i',$cs) ? "\xE2\x9C\x93" : '(not recommended)' ),
'mbstring' => loco_check_extension('mbstring') ? "\xCE\x9F\xCE\x9A \xE2\x9C\x93" : 'No',
) );
// Sanity check mbstring.func_overload
if( 2 !== strlen("\xC2\xA3") ){
$encoding->mbstring = 'Error, disable mbstring.func_overload';
......
......@@ -36,8 +36,6 @@ abstract class Loco_admin_file_BaseController extends Loco_admin_bundle_BaseCont
// security validations
try {
Loco_gettext_Data::ext( $file );
// TODO also need to block access to files outside content directory
// this is more difficult as can symlink into and out of the tree.
}
catch( Exception $e ){
return $this->view( 'admin/errors/file-sec', array( 'reason' => $e->getMessage() ) );
......
......@@ -42,40 +42,6 @@ class Loco_admin_file_EditController extends Loco_admin_file_BaseController {
}
/**
* @param bool whether po files is in read-only mode
* @return array[]|null
*/
private function getApiProviders( $readonly ){
if( $readonly ){
return null;
}
$apis = array_filter( Loco_api_Providers::export(), array(__CLASS__,'filterApiProvider') );
usort($apis,array(__CLASS__,'sortApiProviders') );
return $apis;
}
/**
* @internal
* @param string[]
* @return bool
*/
private static function filterApiProvider( array $api ){
return (bool) $api['key'];
}
/**
* @internal
* @param string[]
* @param string[]
* @return bool
*/
private static function sortApiProviders( array $a, array $b ){
return strcasecmp($a['name'],$b['name']);
}
/**
* {@inheritdoc}
*/
......@@ -113,7 +79,7 @@ class Loco_admin_file_EditController extends Loco_admin_file_BaseController {
// Fine if not, this just means sync isn't possible.
catch( Loco_error_Exception $e ){
Loco_error_AdminNotices::add( $e );
Loco_error_AdminNotices::debug( sprintf("Sync is disabled because this file doesn't relate to a known set of translations", $bundle ) );
Loco_error_AdminNotices::debug("Sync is disabled because this file doesn't relate to a known set of translations");
$project = null;
}
......@@ -214,7 +180,7 @@ class Loco_admin_file_EditController extends Loco_admin_file_BaseController {
'domain' => (string) $project->getId(),
) : null,
'nonces' => $this->getNonces($readonly),
'apis' => $locale ? $this->getApiProviders($readonly) : null,
'apis' => $locale && ! $readonly ? Loco_api_Providers::configured() : null,
) ) );
$this->set( 'ui', new Loco_mvc_ViewParams( array(
// Translators: button for adding a new string when manually editing a POT file
......
......@@ -32,7 +32,7 @@ class Loco_admin_file_InfoController extends Loco_admin_file_BaseController {
* {@inheritdoc}
*/
public function render(){
/* @var Loco_fs_File $file */
$file = $this->get('file');
$name = $file->basename();
$this->set('title', $name );
......@@ -67,6 +67,15 @@ class Loco_admin_file_InfoController extends Loco_admin_file_BaseController {
$dinfo['existent'] = true;
$dinfo['writable'] = $dir->writable();
}
// secure download link
$args = new Loco_mvc_HiddenFields( array (
'route' => 'download',
'action' => 'loco_download',
'path' => $file->getRelativePath(loco_constant('WP_CONTENT_DIR')),
) );
$args->setNonce('download');
$finfo['download'] = $args->getHref( admin_url('admin-ajax.php','relative') );
// collect note worthy problems with file headers
$debugging = loco_debugging();
......@@ -153,8 +162,8 @@ class Loco_admin_file_InfoController extends Loco_admin_file_BaseController {
}
}
if( $debugging ){
// missing or invalid headers are tollerated but developers should be notified
if( $debugging && ! count($head) ){
// missing or invalid headers are tolerated but developers should be notified
if( ! count($head) ){
$debug[] = __('File does not have a valid header','loco-translate');
}
// Language header sanity checks, raising developer (debug) warnings
......
......@@ -26,12 +26,13 @@ class Loco_admin_file_MoveController extends Loco_admin_file_BaseController {
$this->set('hidden',$fields );
// attempt move if valid nonce posted back
while( $this->checkNonce($action) ){
$post = Loco_mvc_PostParams::get();
// Chosen location should be valid as a posted "dest" parameter
if( ! Loco_mvc_PostParams::get()->has('dest') ){
if( ! $post->has('dest') ){
Loco_error_AdminNotices::err('No destination posted');
break;
}
$target = new Loco_fs_LocaleFile( Loco_mvc_PostParams::get()->dest );
$target = new Loco_fs_LocaleFile( $post->dest );
$ext = $target->extension();
// primary file extension should only be permitted to change between po and pot
if( $ext !== $file->extension() && 'po' !== $ext && 'pot' !== $ext ){
......
......@@ -74,9 +74,12 @@ class Loco_admin_init_InitPoController extends Loco_admin_bundle_BaseController
/**
* @internal
* @internal
* @param int
* @param int
* @return int
*/
public static function _onSortLocationKeys( $a, $b ){
private static function compareLocationKeys( $a, $b ){
static $order = array('custom' => 4, 'wplang' => 3, 'theme' => 2, 'plugin' => 2, 'other' => 1 );
$x = $order[$a];
$y = $order[$b];
......@@ -246,9 +249,8 @@ class Loco_admin_init_InitPoController extends Loco_admin_bundle_BaseController
// there is no point checking whether any of these file exist, because we don't know what language will be chosen yet.
$sortable = array();
$locations = array();
$fs_protect = Loco_data_Settings::get()->fs_protect;
$fs_failure = null;
/* @var Loco_fs_File $pofile */
/* @var Loco_fs_LocaleFile $pofile */
foreach( $filechoice as $pofile ){
$parent = new Loco_fs_LocaleDirectory( $pofile->dirname() );
$systype = $parent->getUpdateType();
......@@ -270,6 +272,7 @@ class Loco_admin_init_InitPoController extends Loco_admin_bundle_BaseController
$writable = false;
$disabled = true;
}
$suffix = '-'.$pofile->getSuffix().'.po';
$choice = new Loco_mvc_ViewParams( array (
'checked' => '',
'writable' => $writable,
......@@ -277,18 +280,14 @@ class Loco_admin_init_InitPoController extends Loco_admin_bundle_BaseController
'systype' => $systype,
'parent' => Loco_mvc_FileParams::create( $parent ),
'hidden' => $pofile->getRelativePath($content_dir),
'holder' => str_replace( (string) $locale, '<span>&lt;locale&gt;</span>', $pofile->basename() ),
'holder' => str_replace( $suffix, '-<span>{locale}</span>.po', $pofile->basename() ),
) );
// may need to show system file warnings
if( $systype && $fs_protect ){
$choice['syswarn'] = true;
}
$sortable[] = $choice;
$locations[$typeId]['paths'][] = $choice;
}
// display locations in runtime preference order
uksort( $locations, array(__CLASS__,'_onSortLocationKeys') );
uksort( $locations, array(__CLASS__,'compareLocationKeys') );
$this->set( 'locations', $locations );
// pre-select best (safest/writable) option
......
<?php
/**
* File upload initializer.
* Uploads a PO file to the bundle and compiles MO.
*/
class Loco_admin_init_UploadController extends Loco_admin_bundle_BaseController {
/**
* {@inheritdoc}
**/
public function init() {
parent::init();
// Use Ajax controller for standard postback
if( $this->checkNonce('upload') ){
try {
$ctrl = new Loco_ajax_UploadController;
$ctrl->_init($_POST);
$href = $ctrl->process( Loco_mvc_PostParams::get() );
if( wp_redirect($href) ){
exit;
}
}
catch( Exception $e ){
Loco_error_AdminNotices::add( Loco_error_Exception::convert($e) );
}
}
// Set page title before render sets inline title
$bundle = $this->getBundle();
$this->set('title', __('Upload','loco-translate').' &lsaquo; '.$bundle->getName() );
}
/**
* {@inheritdoc}
*/
public function render(){
// file upload requires a properly configured project
$bundle = $this->getBundle();
$project = $this->getProject();
$fields = new Loco_mvc_HiddenFields( array(
'path' => '',
'auth' => 'upload',
'type' => $bundle->getType(),
'domain' => $project->getId(),
'bundle' => $bundle->getHandle(),
) );
$fields->setNonce('upload');
$this->set('hidden',$fields);
$this->prepareFsConnect('upload','');
// standard bundle navigation with link back to overview
$breadcrumb = $this->prepareNavigation();
$breadcrumb->add( __('Upload a translation file','loco-translate') );
$this->set( 'breadcrumb', $breadcrumb );
// we won't know the locale until the file is uploaded, so use a dummy for location choice
$locale = new Loco_Locale('zxx');
$filechoice = $this->getProject()->initLocaleFiles($locale);
//
$locations = array();
/* @var Loco_fs_LocaleFile $pofile */
foreach( $filechoice as $pofile ){
// initialize location type (system, etc..)
$parent = new Loco_fs_LocaleDirectory( $pofile->dirname() );
$typeId = $parent->getTypeId();
if( ! isset($locations[$typeId]) ){
$locations[$typeId] = new Loco_mvc_ViewParams( array(
'label' => $parent->getTypeLabel($typeId),
'paths' => array(),
) );
}
$locations[$typeId]['paths'][] = new Loco_mvc_ViewParams( array(
'parent' => Loco_mvc_FileParams::create($parent),
'holder' => str_replace('-zxx.po','-{locale}</span>.po', $pofile->basename() ),
) );
}
// we don't know what the specifics will be until a location is chosen and a file is presented.
$this->set('locale',get_locale());
$this->set('locations', $locations );
// file upload will be done via ajax if possible
$settings = Loco_data_Settings::get();
$this->set('js',new Loco_mvc_ViewParams( array (
'multipart' => (bool) $settings->ajax_files,
'nonces' => array( 'upload' => $fields->getNonce() ),
) ) );
$this->enqueueScript('upload');
return $this->view('admin/init/upload');
}
}
\ No newline at end of file
......@@ -13,7 +13,35 @@ class Loco_ajax_ApisController extends Loco_mvc_AjaxController {
// Fire an event so translation apis can register their hooks as lazily as possible
do_action('loco_api_ajax');
// API client id should be posted
// Get request renders API modal contents:
if( 0 === $post->count() ){
$apis = Loco_api_Providers::configured();
$this->set('apis',$apis);
// modal views for batch-translate and suggest feature
$modal = new Loco_mvc_View;
$modal->set('apis',$apis);
// help buttons
$locale = $this->get('locale');
$modal->set( 'help', new Loco_mvc_ViewParams( array (
'text' => __('Help','loco-translate'),
'href' => apply_filters('loco_external','https://localise.biz/wordpress/plugin/manual/providers'),
) ) );
$modal->set('prof', new Loco_mvc_ViewParams( array (
'text' => __('Need a human?','loco-translate'),
'href' => apply_filters('loco_external','https://localise.biz/wordpress/translation?l='.$locale),
) ) );
// render auto-translate modal or prompt for configuration
if( $apis ){
$html = $modal->render('ajax/modal-apis-batch');
}
else {
$html = $modal->render('ajax/modal-apis-empty');
}
$this->set('html',$html);
return parent::render();
}
// else API client id should be posted to perform operation
$hook = (string) $post->hook;
// API client must be hooked in using loco_api_providers filter
......
......@@ -14,12 +14,14 @@ class Loco_ajax_DownloadController extends Loco_mvc_AjaxController {
// we need a path, but it may not need to exist
$file = new Loco_fs_File( $this->get('path') );
$file->normalize( loco_constant( 'WP_CONTENT_DIR') );
$is_binary = 'mo' === strtolower( $file->extension() );
// Restrict download to gettext file formats
$ext = Loco_gettext_Data::ext($file);
// posted source must be clean and must parse as whatever the file extension claims to be
if( $raw = $post->source ){
// compile source if target is MO
if( $is_binary ) {
if( 'mo' === $ext ) {
$raw = Loco_gettext_Data::fromSource($raw)->msgfmt();
}
}
......@@ -28,16 +30,13 @@ class Loco_ajax_DownloadController extends Loco_mvc_AjaxController {
else if( $file->exists() ){
$raw = $file->getContents();
}
/*/ else if PO exists but MO doesn't, we can compile it on the fly
else if( ! $is_binary ){
}*/
// else we can't do anything except bail
else {
throw new Loco_error_Exception('File not found and no source posted');
}
// Observe UTF-8 BOM setting
if( ! $is_binary ){
// Observe UTF-8 BOM setting for PO and POT only
if( 'po' === $ext || 'pot' === $ext ){
$has_bom = "\xEF\xBB\xBF" === substr($raw,0,3);
$use_bom = (bool) Loco_data_Settings::get()->po_utf8_bom;
// only alter file if valid UTF-8. Deferring detection overhead until required
......@@ -51,7 +50,6 @@ class Loco_ajax_DownloadController extends Loco_mvc_AjaxController {
}
}
return $raw;
}
......
......@@ -47,6 +47,7 @@ class Loco_ajax_FsConnectController extends Loco_mvc_AjaxController {
/**
* @param Loco_fs_File file path to update (should exist)
* @return bool
*/
private function authorizeUpdate( Loco_fs_File $file ){
......@@ -57,16 +58,31 @@ class Loco_ajax_FsConnectController extends Loco_mvc_AjaxController {
if( Loco_data_Settings::get()->num_backups && ! $this->api->authorizeCopy($file) ){
return false;
}
// updating file may also recompile binary, which may or may not exist
$files = new Loco_fs_Siblings( $file );
if( $file = $files->getBinary() ){
return $this->api->authorizeSave($file);
// updating file will also recompile binary, which may or may not exist
$files = new Loco_fs_Siblings($file);
$mofile = $files->getBinary();
if( $mofile && ! $this->api->authorizeSave($mofile) ){
return false;
}
// else no dependants to update
return true;
}
/**
* @param Loco_fs_File path which may exist (update it) or may not (create it)
* @return bool
*/
private function authorizeUpload( Loco_fs_File $file ){
if( $file->exists() ){
return $this->api->authorizeUpdate($file);
}
else {
return $this->api->authorizeCreate($file);
}
}
/**
* {@inheritdoc}
*/
......
......@@ -52,26 +52,16 @@ class Loco_ajax_SaveController extends Loco_ajax_common_BundleController {
$data = Loco_gettext_Data::fromSource( $post->data );
}
// WordPress-ize some headers that differ from JavaScript libs
if( $compile = (bool) $locale ){
// WordPress-ize some headers that differ from that sent from JavaScript
if( $locale ){
$head = $data->getHeaders();
$head['Language'] = strtr( $locale, '-', '_' );
}
// backup existing file before overwriting, but still allow if backups fails
$num_backups = Loco_data_Settings::get()->num_backups;
if( $num_backups && $poexists ){
try {
$api->authorizeCopy( $pofile );
$backups = new Loco_fs_Revisions( $pofile );
$backups->create();
$backups->prune($num_backups);
}
catch( Exception $e ){
Loco_error_AdminNotices::debug( $e->getMessage() );
$message = __('Failed to create backup file in "%s". Check file permissions or disable backups','loco-translate');
Loco_error_AdminNotices::warn( sprintf( $message, $pofile->getParent()->basename() ) );
}
// backup existing file before overwriting, but continue if backups fails
if( $poexists ){
$backups = new Loco_fs_Revisions($pofile);
$backups->rotate($api);
}
// commit file directly to disk
......@@ -97,21 +87,20 @@ class Loco_ajax_SaveController extends Loco_ajax_common_BundleController {
$this->set('datetime', Loco_mvc_ViewParams::date_i18n($mtime) );
// Compile MO and JSON files unless saving template
if( $compile ){
if( $locale ){
try {
$mofile = $pofile->cloneExtension('mo');
$api->authorizeSave( $mofile );
$bytes = $mofile->putContents( $data->msgfmt() );
$this->set( 'mobytes', $bytes );
Loco_error_AdminNotices::success( __('PO file saved and MO file compiled','loco-translate') );
}
catch( Exception $e ){
Loco_error_AdminNotices::debug( $e->getMessage() );
Loco_error_AdminNotices::warn( __('PO file saved, but MO file compilation failed','loco-translate') );
$this->set( 'mobytes', 0 );
// prevent further compilation if MO failed
$compile = false;
// $compile = false;
}
}
else {
......
<?php
/**
* Ajax "upload" route, for putting translation files to the server
*/
class Loco_ajax_UploadController extends Loco_ajax_common_BundleController {
/**
* {@inheritdoc}
*/
public function render(){
$post = $this->validate();
$href = $this->process( $post );
//
$this->set('redirect',$href);
return parent::render();
}
/**
* Upload processor shared with standard postback controller
* @param Loco_mvc_ViewParams script input
* @return string redirect to file edit
*/
public function process( Loco_mvc_ViewParams $post ){
$bundle = $this->getBundle();
$project = $this->getProject( $bundle );
// Chosen folder location should be valid as a posted "dir" parameter
if( ! $post->has('dir') ){
throw new Loco_error_Exception('No destination posted');
}
$base = loco_constant('WP_CONTENT_DIR');
$parent = new Loco_fs_Directory($post->dir);
$parent->normalize($base);
// Loco_error_AdminNotices::debug('Destination set to '.$parent->getPath() );
// Ensure file uploaded ok
if( ! isset($_FILES['f']) ){
throw new Loco_error_Exception('No file posted');
}
$upload = new Loco_data_Upload($_FILES['f']);
$dummy = new Loco_fs_DummyFile( $upload->getName() );
$ext = strtolower( $dummy->extension() );
// Loco_error_AdminNotices::debug('Have uploaded file: '.$dummy->basename() );
switch($ext){
case 'po':
case 'mo':
$dummy->putContents($upload->getContents());
$pomo = Loco_gettext_Data::load($dummy);
break;
default:
throw new Loco_error_Exception('Only PO/MO uploads supported');
}
// PO/MO data is valid.
// get real file name and establish if a locale can be extracted, otherwise get from headers
$file = new Loco_fs_LocaleFile( $dummy->basename() );
$locale = $file->getLocale();
if( ! $locale->isValid() ){
$value = $pomo->getHeaders()->offsetGet('Language');
$locale = Loco_Locale::parse($value);
if( ! $locale->isValid() ){
throw new Loco_error_Exception('Unable to detect language from '.$file->basename() );
}
}
// Fail if user presents å wrongly named file. This is to avoid mixing up text domains.
$pofile = $project->initLocaleFile($parent,$locale);
if( $pofile->filename() !== $dummy->filename() ){
throw new Loco_error_Exception( sprintf('File must be named %s', $pofile->filename().'.'.$ext ) );
}
$api = new Loco_api_WordPressFileSystem;
// PO may exist already. If not we need to auth create instead of update
if( $pofile->exists() ){
if( 'po' === $ext && $pofile->md5() === $dummy->md5() ){
throw new Loco_error_Exception( __('Your file is identical to the existing one','loco-translate') );
}
// backup existing PO file before overwriting, but proceed on failure
$backups = new Loco_fs_Revisions($pofile);
$backups->rotate($api);
$api->authorizeUpdate($pofile);
}
else {
$api->authorizeCreate( $pofile );
}
// Putting file contents, because remote file system may not be able to read from tmp/upload location
if( 'mo' === $ext ){
$pofile->putContents( $pomo->msgcat() );
$bin = $dummy->getContents(); // <- use binary as-is.
}
else {
$pofile->putContents( $dummy->getContents() ); // <- use po source as is
$bin = $pomo->msgfmt(); // <- compile binary from PO
}
// should have binary data unless something went wrong
if( $bin ){
$mofile = $pofile->cloneExtension('mo');
$mofile->exists() ? $api->authorizeUpdate($mofile) : $api->authorizeCreate($mofile);
$mofile->putContents($bin);
}
// Redirect to edit this PO. Sync may be required and we're not doing automatically here.
$type = strtolower( $this->get('type') );
return Loco_mvc_AdminRouter::generate( sprintf('%s-file-edit',$type), array(
'path' => $pofile->getRelativePath($base),
'bundle' => $bundle->getHandle(),
'domain' => $project->getId(),
) );
}
}
\ No newline at end of file
......@@ -42,6 +42,38 @@ abstract class Loco_api_Providers {
'key' => $settings->offsetGet('yandex_api_key'),
),
);
}
}
}
\ No newline at end of file
/**
* Get only configured APIs, and sort them fairly
* @return array[]
*/
public static function configured(){
$apis = array_filter( self::export(), array(__CLASS__,'filterConfigured') );
usort( $apis, array(__CLASS__,'compareNames') );
return $apis;
}
/**
* @internal
* @param string[]
* @return bool
*/
private static function filterConfigured( array $api ){
return array_key_exists('key',$api) && is_string($api['key']) && '' !== $api['key'];
}
/**
* @internal
* @param string[]
* @param string[]
* @return bool
*/
private static function compareNames( array $a, array $b ){
return strcasecmp($a['name'],$b['name']);
}
}
......@@ -369,7 +369,7 @@ class Loco_api_WordPressFileSystem {
/**
* Check if a file is safe from WordPress automatic updates
* Check if a file is subject to WordPress automatic updates
* @param Loco_fs_File
* @return bool
*/
......@@ -378,14 +378,32 @@ class Loco_api_WordPressFileSystem {
if( $this->isAutoUpdateDenied() ){
return false;
}
if( apply_filters( 'automatic_updater_disabled', loco_constant('AUTOMATIC_UPDATER_DISABLED') ) ) {
return false;
}
// Auto-updates aren't denied, so ascertain location "type" and run through the same filters as should_update()
if( $type = $file->getUpdateType() ){
// TODO provide a useful context for the update offer passed to filters
// WordPress updater will have taken this from remote API data which we don't have here.
// Auto-updates aren't denied, so ascertain location "type" and run through the same filters as WP_Automatic_Updater::should_update()
$type = $file->getUpdateType();
if( '' !== $type ){
// Since 5.5.0: "{type}_s_auto_update_enabled" filters auto-update status for themes and plugins
// admins must also enable auto-updates on plugins and themes individually, but not checking that here.
if( function_exists('wp_is_auto_update_enabled_for_type') && ('plugin'===$type||'theme'===$type) ){
$enabled = (bool) apply_filters( "{$type}s_auto_update_enabled", true );
if( $enabled ){
// resolve given file to plugin/theme handle so we can check if it's been enabled
$bundle = Loco_package_Bundle::fromFile($file);
if( $bundle instanceof Loco_package_Bundle ){
$handle = $bundle->getHandle();
$option = (array) get_site_option( "auto_update_{$type}s", array() );
// var_dump( compact('handle','option') );
if( ! in_array($handle,$option,true) ){
$enabled = false;
}
}
}
return $enabled;
}
// WordPress updater will have {item} from remote API data which we don't have here.
$item = new stdClass;
$item->new_files = false;
$item->autoupdate = true;
$item->disable_autoupdate = false;
return apply_filters( 'auto_update_'.$type, true, $item );
}
// else safe (not auto-updatable)
......
......@@ -13,7 +13,7 @@ abstract class Loco_compat_Failure {
*/
public static function print_hook_failure(){
$texts = array( 'Loco Translate failed to start up' );
/*/ Hooks currently not using annotatons (would be if we enabled @priority tag)
/*/ Hooks currently not using annotations (would be if we enabled @priority tag)
if( ini_get('opcache.enable') && ( ! ini_get('opcache.save_comments') || ! ini_get('opcache.load_comments') ) ){
$texts[] = 'Try configuring opcache to preserve comments';
}*/
......
......@@ -27,7 +27,7 @@ abstract class Loco_compat_PosixExtension {
}
// else use temp file system to establish owner
else {
self::$uid = self::getuidViaTempDir();
self::$uid = self::getuidViaTempDir(); // @codeCoverageIgnore
}
}
return self::$uid;
......@@ -45,7 +45,7 @@ abstract class Loco_compat_PosixExtension {
}
// else use temp file system to establish group owner
else {
self::$gid = self::getgidViaTempDir();
self::$gid = self::getgidViaTempDir(); // @codeCoverageIgnore
}
}
return self::$gid;
......
......@@ -6,10 +6,16 @@
class Loco_data_Upload {
/**
* Actual file currently on system
* @var Loco_fs_File
*/
private $file;
/**
* @var array
*/
private $data;
/**
* Pass through temporary file data
......@@ -19,7 +25,7 @@
*/
public static function src($key){
$upload = new Loco_data_Upload($_FILES[$key]);
return $upload->file->getContents();
return $upload->getContents();
}
......@@ -28,6 +34,7 @@
* @throws Loco_error_UploadException
*/
public function __construct( array $data ){
$this->data = $data;
// https://www.php.net/manual/en/features.file-upload.errors.php
$code = (int) $data['error'];
switch( $code ){
......@@ -50,10 +57,6 @@
default:
throw new Loco_error_UploadException('Unknown file upload error',$code);
}
// mime check is largely pointless but may as well check as we'll only send one type
if( 'application/x-gettext' !== $data['type'] ){
throw new Loco_error_UploadException('Unsupported file type, expected PO or POT file');
}
// upload is OK according to PHP, but check it's really readable and not empty
$path = $data['tmp_name'];
$file = new Loco_fs_File($path);
......@@ -66,6 +69,22 @@
// file is really ok
$this->file = $file;
}
/**
* @return string
*/
public function getName(){
return $this->data['name'];
}
/**
* @return string
*/
public function getContents(){
return $this->file->getContents();
}
}
\ No newline at end of file
......@@ -119,13 +119,14 @@ class Loco_fs_DummyFile extends Loco_fs_File {
}
/**
* {@inheritdoc}
*/
public function copy( $dest ){
$copy = clone $this;
$copy->path = $dest;
$copy = new Loco_fs_DummyFile($dest);
foreach( get_object_vars($this) as $prop => $value ){
$copy->$prop = $value;
}
return $copy;
}
......@@ -144,9 +145,8 @@ class Loco_fs_DummyFile extends Loco_fs_File {
public function gid(){
return $this->gid;
}
/**
* {@inheritdoc}
* @codeCoverageIgnore
......@@ -167,6 +167,14 @@ class Loco_fs_DummyFile extends Loco_fs_File {
}
// else locked:
return false;
}
}
/**
* {@inheritDoc}
*/
public function md5(){
return md5( $this->getContents() );
}
}
\ No newline at end of file
......@@ -621,7 +621,8 @@ class Loco_fs_File {
public function getUpdateType(){
// global languages directory root, and canonical subdirectories
$dirpath = (string) ( $this->isDirectory() ? $this : $this->getParent() );
if( $sub = Loco_fs_Locations::getGlobal()->rel($dirpath) ){
$sub = Loco_fs_Locations::getGlobal()->rel($dirpath);
if( is_string($sub) && '' !== $sub ){
list($root) = explode('/', $sub, 2 );
if( '.' === $root || 'themes' === $root || 'plugins' === $root ){
return 'translation';
......@@ -641,5 +642,19 @@ class Loco_fs_File {
// else not an update type
return '';
}
/**
* Get MD5 hash of file contents
* @return string
*/
public function md5(){
if( $this->exists() ) {
return md5_file( $this->path );
}
else {
return 'd41d8cd98f00b204e9800998ecf8427e';
}
}
}
......@@ -111,6 +111,7 @@ class Loco_fs_Locations extends ArrayObject {
if( ! self::$plugin ){
self::$plugin = new Loco_fs_Locations( array(
loco_constant('WP_PLUGIN_DIR'),
loco_constant('WPMU_PLUGIN_DIR'),
) );
}
return self::$plugin;
......
......@@ -6,7 +6,7 @@
class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
/**
* @var loco_fs_File
* @var Loco_fs_File
*/
private $master;
......@@ -140,7 +140,6 @@ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
}
/**
* @return array
*/
......@@ -163,7 +162,6 @@ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
}
/**
* Parse a file path into a timestamp
* @param string
......@@ -179,7 +177,6 @@ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
}
/**
* Get number of backups plus master
* @return int
......@@ -192,7 +189,6 @@ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
}
/**
* Delete file when object removed from memory.
* Previously unlinked on shutdown, but doesn't work with WordPress file system abstraction
......@@ -204,24 +200,29 @@ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ {
}
/**
* Test whether at least one backup file exists on disk.
* @return bool
*
public function sniff(){
$found = false;
if( $dir = opendir( $this->master->dirname() ) ){
$regex = $this->getRegExp();
while( $f = readdir($dir) ){
if( preg_match($regex,$f) ){
$found = true;
break;
}
* Execute backup of current file.
* @param Loco_api_WordPressFileSystem Authorized file system
* @return bool whether rotation
*/
public function rotate( Loco_api_WordPressFileSystem $api ){
$pofile = $this->master;
// backup existing file before overwriting, but still allow if backups fails
$num_backups = Loco_data_Settings::get()->num_backups;
if( $num_backups ){
try {
$api->authorizeCopy($this->master);
$this->create();
$this->prune($num_backups);
return true;
}
catch( Exception $e ){
Loco_error_AdminNotices::debug( $e->getMessage() );
$message = __('Failed to create backup file in "%s". Check file permissions or disable backups','loco-translate');
Loco_error_AdminNotices::warn( sprintf( $message, $pofile->getParent()->basename() ) );
}
closedir($dir);
}
return $found;
}*/
return false;
}
}
......@@ -10,12 +10,13 @@ class Loco_gettext_Data extends LocoPoIterator implements JsonSerializable {
/**
* Normalize file extension to internal type
* @param Loco_fs_File
* @return string "po", "pot" or "mo"
* @return string Normalized file extension "po", "pot" or "mo"
* @throws Loco_error_Exception
*/
public static function ext( Loco_fs_File $file ){
$ext = rtrim( strtolower( $file->extension() ), '~' );
if( 'po' === $ext || 'pot' === $ext || 'mo' === $ext ){
// We could validate file location here, but file type restriction should be sufficient
return $ext;
}
// translators: Error thrown when attempting to parse a file that is not PO, POT or MO
......
......@@ -88,18 +88,20 @@ class Loco_gettext_Extraction {
if( function_exists('wp_raise_memory_limit') ){
wp_raise_memory_limit('loco');
}
/* @var $file Loco_fs_File */
/* @var Loco_fs_File $file */
foreach( $project->findSourceFiles() as $file ){
$type = $opts->ext2type( $file->extension() );
$extr = loco_wp_extractor($type);
if( 'js' !== $type ) {
// skip large files for PHP, because token_get_all is hungry
$size = $file->size();
$this->maxbytes = max( $this->maxbytes, $size );
if( $size > $max ){
$list = $this->skipped or $list = ( $this->skipped = new Loco_fs_FileList() );
$list->add( $file );
continue;
if( 0 !== $max ){
$size = $file->size();
$this->maxbytes = max( $this->maxbytes, $size );
if( $size > $max ){
$list = $this->skipped or $list = ( $this->skipped = new Loco_fs_FileList() );
$list->add( $file );
continue;
}
}
// extract headers from theme PHP files in
if( $project->getBundle()->isTheme() ){
......
......@@ -192,8 +192,9 @@ class Loco_mvc_AdminRouter extends Loco_hooks_Hookable {
'{type}-debug' => 'bundle_Debug',
'lang-view' => 'bundle_Locale',
// file initialization
'{type}-msginit' => 'init_InitPo',
'{type}-xgettext' => 'init_InitPot',
'{type}-msginit' => 'init_InitPo',
'{type}-xgettext' => 'init_InitPot',
'{type}-upload' => 'init_Upload',
// file resource views
'{type}-file-view' => 'file_View',
'{type}-file-edit' => 'file_Edit',
......
......@@ -28,6 +28,14 @@ class Loco_mvc_HiddenFields extends Loco_mvc_ViewParams {
}
/**
* @return string
*/
public function getNonce() {
return $this['loco-nonce'];
}
/**
* Load postdata fields
* @param Loco_mvc_PostParams post data
......@@ -39,5 +47,17 @@ class Loco_mvc_HiddenFields extends Loco_mvc_ViewParams {
}
return $this;
}
/**
* Append arguments to a URL
* @param string optional base url
* @return string full URL with query string
*/
public function getHref( $base = '' ){
$query = http_build_query($this->getArrayCopy(),null,'&');
$sep = false === strpos($base,'?') ? '?' : '&';
return $base.$sep.$query;
}
}
......@@ -56,8 +56,8 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
/**
* Get system (i.e. "global") target locations for all projects of this type.
* These are aways append to configs, and always excluded from serialization
* @return array<string> absolute directory paths
* These are always append to configs, and always excluded from serialization
* @return string[] absolute directory paths
*/
abstract public function getSystemTargets();
......@@ -108,7 +108,28 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
return $bundle;
}
/**
* Resolve a file path to a plugin, theme or the core
* @param Loco_fs_File
* @return Loco_package_Bundle|null
*/
public static function fromFile( Loco_fs_File $file ){
if( $file->underThemeDirectory() ){
return Loco_package_Theme::fromFile($file);
}
else if( $file->underPluginDirectory() ){
return Loco_package_Plugin::fromFile($file);
}
else if( $file->underWordPressDirectory() && ! $file->underContentDirectory() ){
return Loco_package_Core::create();
}
else {
return null;
}
}
/**
* Construct from WordPress handle and friendly name
* @param string
......@@ -516,7 +537,7 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
/**
* Do basic configuration from bundle meta data (file headers)
* @param array header tags from theme or plugin bootstrapper
* @param array header tags from theme or plugin bootstrap file
* @return bool whether configured
*/
public function configureMeta( array $header ){
......@@ -539,16 +560,15 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
if( isset($header['DomainPath']) && ( $path = trim($header['DomainPath'],'/') ) ){
$project->addTargetDirectory( $base.'/'.$path );
}
else if( $this->solo ){
// skip
}
// else use standard language path if it exists
else if( is_dir($base.'/languages') ) {
$project->addTargetDirectory($base.'/languages');
}
// else add bundle root by default
else {
$project->addTargetDirectory( $base );
else if( ! $this->solo ){
if( is_dir($base.'/languages') ) {
$project->addTargetDirectory($base.'/languages');
}
// else add bundle root by default
else {
$project->addTargetDirectory($base);
}
}
// single file bundles can have only one source file
if( $this->solo ){
......@@ -576,6 +596,8 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
* Configure bundle from canonical sources.
* Source order is "db","file","meta" where meta is the auto-config fallback.
* No deep scanning is performed at this point
* @param string
* @param string[] header tags from theme or plugin bootstrap file
* @return Loco_package_Bundle
*/
public function configure( $base, array $header ){
......@@ -587,18 +609,20 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
/**
* Get the custom config saved in WordPress DB for this bundle
* @return Loco_config_CustomSaved
* @return Loco_config_CustomSaved|null
*/
public function getCustomConfig(){
$custom = new Loco_config_CustomSaved;
if( $custom->setBundle($this)->fetch() ){
return $custom;
}
return null;
}
/**
* Inherit another bundle. Used for child themes to display parent translations
* @param Loco_package_Bundle
* @return Loco_package_Bundle
*/
public function inherit( Loco_package_Bundle $parent ){
......@@ -634,11 +658,11 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
/**
* @return Loco_package_Project
* @return Loco_package_Project|null
*/
public function getDefaultProject(){
$i = 0;
/* @var $project Loco_package_Project */
/* @var Loco_package_Project $project */
foreach( $this as $project ){
if( $project->isDomainDefault() ){
return $project;
......@@ -649,6 +673,7 @@ abstract class Loco_package_Bundle extends ArrayObject implements JsonSerializab
if( 1 === $i ){
return $project;
}
return null;
}
......
......@@ -238,5 +238,32 @@ class Loco_package_Plugin extends Loco_package_Bundle {
return $bundle;
}
/**
* {@inheritDoc}
*/
public static function fromFile( Loco_fs_File $file ){
$find = $file->getPath();
foreach( self::get_plugins() as $handle => $data ){
$boot = new Loco_fs_File( $handle );
$boot->normalize( $data['basedir'] );
// single file plugins can only match if given file is the plugin file itself.
if( basename($handle) === $handle ){
if( $boot->getPath() === $file ){
return self::create($handle);
}
}
// else check file is under plugin root.
else {
$base = $boot->dirname();
$path = $base.substr( $find, strlen($base) );
if( $path === $find ){
return self::create($handle);
}
}
}
return null;
}
}
\ No newline at end of file
}
......@@ -731,23 +731,19 @@ class Loco_package_Project {
$suffix = sprintf( '%s.po', $locale );
$prefix = $slug ? sprintf('%s-',$slug) : '';
$choice = new Loco_fs_FileList;
/* @var $dir Loco_fs_Directory */
/* @var Loco_fs_Directory $dir */
foreach( $this->getConfiguredTargets() as $dir ){
// theme files under their own directory normally have no file prefix
if( $default && $dir->underThemeDirectory() ){
$path = $dir->getPath().'/'.$suffix;
}
// plugin files are prefixed even in their own directory, so empty prefix here implies incorrect bundle configuration
//else if( $default && ! $prefix && $dir->underPluginDirectory() ){
// $path = $dir->getPath().'/'.$domain.'-'.$suffix;
//}
// all other paths use configured prefix, which may be empty
else {
$path = $dir->getPath().'/'.$prefix.$suffix;
}
$choice->add( new Loco_fs_LocaleFile($path) );
}
/* @var $dir Loco_fs_Directory */
/* @var Loco_fs_Directory $dir */
foreach( $this->getSystemTargets() as $dir ){
$path = $dir->getPath();
// themes and plugins under global locations will be loaded by domain, regardless of prefix
......@@ -765,6 +761,26 @@ class Loco_package_Project {
}
/**
* Initialize a PO file path from required location
* @param Loco_fs_Directory
* @param Loco_Locale
* @return Loco_fs_LocaleFile
* @throws Loco_error_Exception
*/
public function initLocaleFile( Loco_fs_Directory $dir, Loco_Locale $locale ){
$choice = $this->initLocaleFiles($locale);
$pattern = '!^'.preg_quote($dir->getPath(),'!').'/[^/.]+\\.po$!';
/* @var Loco_fs_LocaleFile $file */
foreach( $choice as $file ){
if( preg_match($pattern,$file->getPath()) ){
return $file;
}
}
throw new Loco_error_Exception('Unexpected file location: '.$dir );
}
/**
* Get newest timestamp of all translation files (includes template, but exclude source files)
* @return int
......
......@@ -75,16 +75,18 @@ class Loco_package_Theme extends Loco_package_Bundle {
* Create theme bundle definition from WordPress theme handle
*
* @param string short name of theme, e.g. "twentyfifteen"
* @return Loco_package_Plugin
* @param string theme root if known
* @return Loco_package_Theme
*/
public static function create( $slug, $root = null ){
public static function create( $slug, $root = '' ){
return self::createFromTheme( wp_get_theme( $slug, $root ) );
}
/**
* Create theme bundle definition from WordPress theme data
* @param WP_Theme
* @return Loco_package_Theme
*/
public static function createFromTheme( WP_Theme $theme ){
$slug = $theme->get_stylesheet();
......@@ -130,5 +132,22 @@ class Loco_package_Theme extends Loco_package_Bundle {
// do_action( 'loco_bundle_configured', $bundle );
return $bundle;
}
}
\ No newline at end of file
}
/**
* {@inheritDoc}
*/
public static function fromFile( Loco_fs_File $file ){
$find = $file->getPath();
foreach( wp_get_themes( array('errors'=>null) ) as $theme ){
$base = $theme->get_stylesheet_directory();
$path = $base.substr( $find, strlen($base) );
if( $find === $path ){
return self::createFromTheme($theme);
}
}
return null;
}
}
......@@ -36,7 +36,9 @@ abstract class Loco_test_WordPressTestCase extends WP_UnitTestCase {
*/
protected static function dropOptions(){
global $wpdb;
$query = $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%s' OR option_name LIKE '%s'", array('loco_%','_%_loco_%') );
$args = array('loco_%','_%_loco_%','%_auto_update_%');
$query = $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%s' OR option_name LIKE '%s' OR option_name LIKE '%s';", $args );
if( $results = $wpdb->get_results($query,ARRAY_N) ){
foreach( $results as $row ){
list( $option_name ) = $row;
......@@ -439,6 +441,8 @@ abstract class Loco_test_WordPressTestCase extends WP_UnitTestCase {
/**
* @param int
* @param string
* @return string location
*/
public function assertRedirected( $status = 302, $message = 'Failed to redirect' ){
......@@ -451,18 +455,21 @@ abstract class Loco_test_WordPressTestCase extends WP_UnitTestCase {
/**
* Set $_POST
* @param string[]
* @return void
*/
public function setPostArray( array $post ){
$_POST = $post;
$_REQUEST = array_merge( $_GET, $_POST, $_COOKIE );
$_SERVER['REQUEST_METHOD'] = 'POST';
$_FILES = array();
Loco_mvc_PostParams::destroy();
}
/**
* Augment $_POST
* @param string[]
* @return void
*/
public function addPostArray( array $post ){
......@@ -472,21 +479,47 @@ abstract class Loco_test_WordPressTestCase extends WP_UnitTestCase {
/**
* Set $_GET
* @param string[]
* @return void
*/
public function setGetArray( array $get ){
$_GET = $get;
$_REQUEST = array_merge( $_GET, $_POST, $_COOKIE );
$_SERVER['REQUEST_METHOD'] = 'GET';
$_FILES = array();
}
/**
* Augment $_GET
* @param string[]
* @return void
*/
public function addGetArray( array $get ){
$this->setGetArray( $get + $_GET );
}
/**
* @param string _FILES key
* @param string real file on local system that would be uploaded
*/
public function addFileUpload( $key, $path ){
if( 'POST' !== $_SERVER['REQUEST_METHOD'] ){
throw new LogicException('Set POST method before adding to files collection');
}
$src = file_get_contents($path);
$tmp = tempnam(LOCO_TEST_DATA_ROOT.'/tmp','phpunit');
$len = file_put_contents( $tmp, $src);
if( $len !== strlen($src) ){
throw new Exception('Bad file params');
}
$_FILES[$key] = array (
'error' => 0,
'tmp_name' => $tmp,
'name' => basename($path),
);
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@
$this->extend('../layout');
/* @var Loco_mvc_ViewParams $versions */
/* @var Loco_mvc_ViewParams $encoding */
?>
<div class="panel" id="loco-versions">
......@@ -35,7 +36,10 @@ $this->extend('../layout');
<dd><?php echo $encoding->OK?> <span id="loco-utf8-check"><?php echo $encoding->tick?></span></dd>
<dt>Multibyte support:</dt>
<dd><?php echo $encoding->mbstring?></dd>
<dd><?php $encoding->e('mbstring')?></dd>
<dt>Site character set</dt>
<dd><?php $encoding->e('charset')?></dd>
</dl>
</div>
......
......@@ -6,64 +6,3 @@ $this->extend('editor');
$this->start('header');
echo $this->render('../common/inc-po-header');
/* @var Loco_mvc_ViewParams $js */
$help = apply_filters('loco_external','https://localise.biz/wordpress/plugin/manual/providers');
// inline modal for auto-translate. Note that modal will be placed outside of #loco.wrap element
if( $js->apis ):?>
<div id="loco-auto" class="loco-batch" title="<?php esc_html_e('Auto-translate this file','loco-translate');?>">
<form action="#">
<fieldset>
<select name="api" id="auto-api"><?php foreach( $js->apis as $api ):?>
<option value="<?php self::e($api['id'])?>"><?php self::e($api['name'])?></option><?php
endforeach?>
</select>
</fieldset>
<fieldset>
<p>
<label for="auto-existing">
<input type="checkbox" id="auto-existing" name="existing" />
<?php esc_html_e('Overwrite existing translations','loco-translate')?>
</label>
</p>
<p>
<label for="auto-fuzzy">
<input type="checkbox" id="auto-fuzzy" name="fuzzy" />
<?php esc_html_e('Mark new translations as Fuzzy','loco-translate')?>
</label>
</p>
<blockquote id="loco-job-progress">
Initializing...
</blockquote>
<p>
<button type="submit" class="button button-primary has-icon icon-translate">
<span><?php esc_html_e('Translate','loco-translate')?></span>
</button>
<a href="<?php self::e($help)?>" class="button button-link has-icon icon-help" target="_blank"><?php
esc_html_e('Help','loco-translate');
?></a>
</p>
</fieldset>
</form>
</div><?php
// inline modal for when no APIs are configured.
else:?>
<div id="loco-auto" class="loco-alert" title="<?php esc_html_e('No translation APIs configured','loco-translate');?>">
<p>
<?php esc_html_e('Add automatic translation services in the plugin settings.','loco-translate')?>
</p>
<nav>
<a href="<?php $this->route('config-apis')->e('href')?>" class="has-icon icon-cog"><?php
esc_html_e('Settings','loco-translate');
?></a>
<a href="<?php self::e($help)?>" class="has-icon icon-help" target="_blank"><?php
esc_html_e('Help','loco-translate');
?></a>
</nav>
</div><?php
endif;
......@@ -9,12 +9,13 @@ echo $header;
/* @var Loco_mvc_ViewParams $js */
/* @var Loco_mvc_ViewParams $ui */
/* @var Loco_mvc_ViewParams $params */
/* @var Loco_mvc_ViewParams $locale */
/* @var Loco_mvc_HiddenFields $dlFields */
?>
<div id="loco-editor">
<nav id="loco-toolbar" class="wp-core-ui">
<nav class="wp-core-ui">
<form action="#" id="loco-actions">
<fieldset>
<button class="button has-icon icon-save" data-loco="save" disabled>
......
......@@ -5,10 +5,14 @@
$this->extend('info');
$this->start('header');
/* @var Loco_mvc_FileParams $file */
/* @var Loco_mvc_FileParams $locale */
/* @var Loco_gettext_Metadata $meta */
?>
<div class="notice inline notice-info">
<nav>
<a class="icon only-icon icon-download" title="Download" href="<?php $file->e('download')?>"><span>download</span></a>
</nav>
<h3>
<a href="<?php $locale->e('href')?>" class="has-lang">
<span class="<?php $locale->e('icon')?>" lang="<?php $locale->e('lang')?>"><code><?php $locale->e('code')?></code></span>
......
......@@ -9,7 +9,12 @@ $this->start('header');
?>
<div class="notice inline notice-info">
<h3><?php esc_html_e('Template file','loco-translate')?></h3>
<nav>
<a class="icon only-icon icon-download" title="Download" href="<?php $file->e('download')?>"><span>download</span></a>
</nav>
<h3>
<?php esc_html_e('Template file','loco-translate')?>
</h3>
<dl>
<dt><?php self::e( __('File size','loco-translate') )?>:</dt>
<dd><?php $file->e('size')?></dd>
......
......@@ -4,9 +4,9 @@
*/
$this->extend('../layout');
echo $header;
/* @var Loco_mvc_FileParams $file */
?>
<?php
if( ! $file->existent ):?>
<div class="notice inline notice-error">
......
......@@ -21,7 +21,7 @@ $this->start('source');
<p class="description"><?php $location->e('label')?>:</p>
</td>
<td><?php
/* @var Loco_mvc_FileParams $choice */
/* @var Loco_mvc_ViewParams $choice */
foreach( $location['paths'] as $choice ):?>
<p><?php
if( $choice->active ):?>
......
......@@ -5,7 +5,7 @@
$this->extend('../layout');
?>
<form action="" method="post" enctype="application/x-www-form-urlencoded" id="loco-move"><?php
<form action="" method="post" enctype="application/x-www-form-urlencoded" id="loco-main"><?php
/* @var Loco_mvc_HiddenFields $hidden */
$hidden->_e();
echo $source?>
......
......@@ -9,5 +9,8 @@
<a href="<?php echo esc_url( apply_filters('loco_external','https://wordpress.org/plugins/loco-translate/') )?>" target="_blank"><?php esc_html_e('Official plugin page','loco-translate')?></a>
</p>
<p>
<a href="<?php echo esc_url( apply_filters('loco_external','https://localise.biz/wordpress/plugin') )?>" target="_blank"><?php esc_html_e('Help and tutorials','loco-translate')?></a>
<a href="<?php echo esc_url( apply_filters('loco_external','https://wordpress.org/support/plugin/loco-translate/') )?>" target="_blank"><?php esc_html_e('Support forum','loco-translate')?></a>
</p>
<p>
<a href="<?php echo esc_url( apply_filters('loco_external','https://localise.biz/wordpress/plugin') )?>" target="_blank"><?php esc_html_e('Info and tutorials','loco-translate')?></a>
</p>
\ No newline at end of file
<?php
/**
* Generic upload template for installing a file into a translation set.
*/
$this->extend('../layout');
/* @var Loco_mvc_ViewParams $params */
?>
<form action="" method="post" enctype="multipart/form-data" id="loco-main"><?php
/* @var Loco_mvc_HiddenFields $hidden */
$hidden->_e();?>
<div class="notice inline notice-generic">
<h2>
<?php self::e( __('Choose a location','loco-translate') );?>
</h2>
<table class="form-table">
<tbody class="loco-paths"><?php
/* @var Loco_mvc_ViewParams[] $locations */
foreach( $locations as $typeId => $location ):?>
<tr class="compact">
<td>
<p class="description"><?php $location->e('label')?>:</p>
</td>
<td><?php
/* @var Loco_mvc_ViewParams $choice */
/* @var Loco_mvc_FileParams $parent */
foreach( $location['paths'] as $choice ):
$parent = $choice['parent'];?>
<p>
<label>
<input type="radio" name="dir" value="<?php $parent->e('relpath')?>" />
<code class="path"><?php $parent->e('relpath')?>/<?php echo $choice->holder?></code>
</label>
</p><?php
endforeach?>
</td>
</tr><?php
endforeach?>
</tbody>
</table>
</div>
<div class="notice inline notice-info">
<h2>
Upload PO file
</h2>
<p>
Your file must be named as shown above where <code>{locale}</code> is the language code, e.g. <code><?php $params->e('locale')?></code>.
</p>
<p>
<input type="file" name="f" />
</p>
<p class="submit">
<button type="submit" class="button button-large button-primary has-icon icon-upload" disabled><?php esc_html_e('Upload','loco-translate')?></button>
</p>
</div>
</form>
<?php
/*
* Modal for batch translate of PO file currently in editor
*/
/* @var Loco_mvc_ViewParams $help */
/* @var Loco_mvc_ViewParams $prof */
/* @var array[] $apis */
?><div id="loco-auto" class="loco-batch" title="<?php esc_html_e('Auto-translate this file','loco-translate');?>">
<form action="#">
<fieldset>
<select name="api" id="auto-api"><?php
foreach( $apis as $a ): $api = new Loco_mvc_ViewParams($a);?>
<option value="<?php $api->e('id')?>"><?php $api->e('name')?></option><?php
endforeach?>
</select>
</fieldset>
<fieldset>
<p>
<label for="auto-existing">
<input type="checkbox" id="auto-existing" name="existing" />
<?php esc_html_e('Overwrite existing translations','loco-translate')?>
</label>
</p>
<p>
<label for="auto-fuzzy">
<input type="checkbox" id="auto-fuzzy" name="fuzzy" />
<?php esc_html_e('Mark new translations as Fuzzy','loco-translate')?>
</label>
</p>
<blockquote id="loco-job-progress">
Initializing...
</blockquote>
<p>
<button type="submit" class="button button-primary has-icon icon-translate">
<span><?php esc_html_e('Translate','loco-translate')?></span>
</button>
<a href="<?php $help->e('href')?>" class="button button-link has-icon icon-help" target="_blank"><?php
$help->e('text');
?></a>
<a href="<?php $prof->e('href')?>" class="button button-link has-icon icon-group" target="_blank"><?php
$prof->e('text');
?></a>
</p>
</fieldset>
</form>
</div>
\ No newline at end of file
<?php
/*
* Modal for when no APIs are configured.
*/
/* @var Loco_mvc_ViewParams $help */
/* @var Loco_mvc_ViewParams $prof */
?><div id="loco-auto" class="loco-alert" title="<?php esc_html_e('No translation APIs configured','loco-translate');?>">
<p>
<?php esc_html_e('Add automatic translation services in the plugin settings.','loco-translate')?>
</p>
<nav>
<a href="<?php $this->route('config-apis')->e('href')?>" class="button button-link has-icon icon-cog"><?php
esc_html_e('Settings','loco-translate');
?></a>
<a href="<?php $help->e('href')?>" class="button button-link has-icon icon-help" target="_blank"><?php
$help->e('text');
?></a>
<a href="<?php $prof->e('href')?>" class="button button-link has-icon icon-group" target="_blank"><?php
$prof->e('text');
?></a>
</nav>
</div>
\ No newline at end of file
<?php
/**
/*
* Debug snippet: dumps current argument scope
* Use from controller by returning $this->view('debug/dump',..)
*/
?><dl class="debug"><?php
/* @var Traversable $params */
foreach( $params as $prop => $value ): if( '_' !== substr($prop,0,1) ):?>
<dt><?php echo esc_html($prop)?></dt>
<dd><?php echo esc_html( json_encode($value,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE) )?></dd><?php
endif; endforeach?>
</dl>
\ No newline at end of file
</dl>
......@@ -43,23 +43,6 @@ function wpseo_set_option() {
add_action( 'wp_ajax_wpseo_set_option', 'wpseo_set_option' );
/**
* Sets an option in the database to hide the index warning for a week.
*
* This function is used in AJAX calls and dies on exit.
*/
function wpseo_set_indexation_remind() {
if ( ! current_user_can( 'manage_options' ) ) {
die( '-1' );
}
check_ajax_referer( 'wpseo-indexation-remind' );
WPSEO_Options::set( 'indexation_warning_hide_until', ( time() + WEEK_IN_SECONDS ) );
die( '1' );
}
add_action( 'wp_ajax_wpseo_set_indexation_remind', 'wpseo_set_indexation_remind' );
/**
* Since 3.2 Notifications are dismissed in the Notification Center.
*/
......@@ -78,11 +61,6 @@ function wpseo_set_ignore() {
$ignore_key = sanitize_text_field( filter_input( INPUT_POST, 'option' ) );
WPSEO_Options::set( 'ignore_' . $ignore_key, true );
if ( $ignore_key === 'indexation_warning' ) {
WPSEO_Options::set( 'indexing_reason', '' );
WPSEO_Options::set( 'indexation_warning_hide_until', false );
}
die( '1' );
}
......
......@@ -15,14 +15,14 @@ class WPSEO_Gutenberg_Compatibility {
*
* @var string
*/
const CURRENT_RELEASE = '9.1.1';
const CURRENT_RELEASE = '9.2.0';
/**
* The minimally supported version of Gutenberg by the plugin.
*
* @var string
*/
const MINIMUM_SUPPORTED = '9.1.1';
const MINIMUM_SUPPORTED = '9.2.0';
/**
* Holds the current version.
......
......@@ -8,7 +8,6 @@
use Yoast\WP\SEO\Config\Schema_Types;
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception;
use Yoast\WP\SEO\Exceptions\SEMrush\Tokens\Empty_Token_Exception;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Config\SEMrush_Client;
use Yoast\WP\SEO\Exceptions\SEMrush\Tokens\Empty_Property_Exception;
......@@ -54,7 +53,6 @@ class WPSEO_Metabox_Formatter {
$analysis_seo = new WPSEO_Metabox_Analysis_SEO();
$analysis_readability = new WPSEO_Metabox_Analysis_Readability();
$schema_types = new Schema_Types();
$options = new Options_Helper();
return [
'author_name' => get_the_author_meta( 'display_name' ),
......@@ -85,7 +83,7 @@ class WPSEO_Metabox_Formatter {
'wordFormRecognitionActive' => YoastSEO()->helpers->language->is_word_form_recognition_active( WPSEO_Language_Utils::get_language( get_locale() ) ),
'siteIconUrl' => get_site_icon_url(),
'countryCode' => WPSEO_Options::get( 'semrush_country_code', false ),
'SEMrushLoginStatus' => WPSEO_Options::get( 'semrush_integration_active', true ) ? $this->get_semrush_login_status( $options ) : false,
'SEMrushLoginStatus' => WPSEO_Options::get( 'semrush_integration_active', true ) ? $this->get_semrush_login_status() : false,
'showSocial' => [
'facebook' => WPSEO_Options::get( 'opengraph', false ),
'twitter' => WPSEO_Options::get( 'twitter', false ),
......@@ -95,7 +93,7 @@ class WPSEO_Metabox_Formatter {
'pageTypeOptions' => $schema_types->get_page_type_options(),
'articleTypeOptions' => $schema_types->get_article_type_options(),
],
'twitterCardType' => $options->get( 'twitter_card_type' ),
'twitterCardType' => YoastSEO()->helpers->options->get( 'twitter_card_type' ),
/**
* Filter to determine if the markers should be enabled or not.
......@@ -292,13 +290,11 @@ class WPSEO_Metabox_Formatter {
/**
* Checks if the user is logged in to SEMrush.
*
* @param {Options_Helper} $options_helper The Options Helper object.
*
* @return boolean The SEMrush login status.
*/
private function get_semrush_login_status( $options_helper ) {
private function get_semrush_login_status() {
try {
$semrush_client = new SEMrush_Client( $options_helper );
$semrush_client = YoastSEO()->classes->get( SEMrush_Client::class );
} catch ( Empty_Property_Exception $e ) {
// return false if token is malformed (empty property).
return false;
......
......@@ -161,6 +161,8 @@ class WPSEO_Tracking_Settings_Data implements WPSEO_Collection {
'semrush_integration_active',
'semrush_tokens',
'semrush_country_code',
'enable_enhanced_slack_sharing',
'zapier_integration_active',
];
/**
......
......@@ -153,6 +153,14 @@ class Yoast_Feature_Toggles {
),
'order' => 100,
],
(object) [
'name' => __( 'Enhanced Slack sharing', 'wordpress-seo' ),
'setting' => 'enable_enhanced_slack_sharing',
'label' => __( 'This adds an author byline and reading time estimate to the article’s snippet when shared on Slack.', 'wordpress-seo' ),
'read_more_label' => __( 'Find out how a rich snippet can improve visibility and click-through-rate.', 'wordpress-seo' ),
'read_more_url' => 'https://yoa.st/help-slack-share',
'order' => 105,
],
];
/**
......
......@@ -13,6 +13,8 @@ if ( ! defined( 'WPSEO_VERSION' ) ) {
exit();
}
echo '<div class="tab-block">';
/*
* {@internal Important: Make sure the options added to the array here are in line with the
* options set in the WPSEO_Option_MS::$allowed_access_options property.}}
......@@ -50,3 +52,5 @@ else {
}
echo '<p><strong>' . esc_html__( 'Take note:', 'wordpress-seo' ) . '</strong> ' . esc_html__( 'Privacy sensitive (FB admins and such), theme specific (title rewrite) and a few very site specific settings will not be imported to new sites.', 'wordpress-seo' ) . '</p>';
echo '</div>';
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment