mirror of
https://github.com/mainwp/mainwp-child.git
synced 2025-09-01 03:58:07 +08:00
Merge with branch01 + new version
This commit is contained in:
parent
f672a41318
commit
1b03e47300
9 changed files with 145 additions and 62 deletions
|
@ -354,12 +354,15 @@ class MainWP_Child_Back_WP_Up {
|
|||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><td>' . __( 'Log folder:', 'backwpup' ) . '</td><td>';
|
||||
if ( ! is_dir( get_site_option( 'backwpup_cfg_logfolder' ) ) ) {
|
||||
echo sprintf( __( 'Logs folder %s not exist.', 'backwpup' ), esc_html( get_site_option( 'backwpup_cfg_logfolder' ) ) );
|
||||
} elseif ( ! is_writable( get_site_option( 'backwpup_cfg_logfolder' ) ) ) {
|
||||
echo sprintf( __( 'Log folder %s is not writable.', 'backwpup' ), esc_html( get_site_option( 'backwpup_cfg_logfolder' ) ) );
|
||||
|
||||
$log_folder = BackWPup_File::get_absolute_path( get_site_option( 'backwpup_cfg_logfolder' ) );
|
||||
|
||||
if ( ! is_dir( $log_folder ) ) {
|
||||
echo sprintf( __( 'Logs folder %s not exist.', 'backwpup' ), esc_html( $log_folder ) );
|
||||
} elseif ( ! is_writable( $log_folder ) ) {
|
||||
echo sprintf( __( 'Log folder %s is not writable.', 'backwpup' ), esc_html( $log_folder ) );
|
||||
} else {
|
||||
echo esc_html( get_site_option( 'backwpup_cfg_logfolder' ) );
|
||||
echo esc_html( $log_folder );
|
||||
}
|
||||
echo '</td></tr>';
|
||||
echo '<tr title=""><td>' . __( 'Server', 'backwpup' ) . '</td><td>' . esc_html( $_SERVER['SERVER_SOFTWARE'] ) . '</td></tr>';
|
||||
|
@ -427,7 +430,9 @@ class MainWP_Child_Back_WP_Up {
|
|||
return array( 'error' => __( 'Missing logfile.', $this->plugin_translate ) );
|
||||
}
|
||||
|
||||
$dir = get_site_option( 'backwpup_cfg_logfolder' );
|
||||
$dir = get_site_option( 'backwpup_cfg_logfolder' );
|
||||
$dir = BackWPup_File::get_absolute_path( $dir );
|
||||
|
||||
foreach ( $_POST['settings']['logfile'] as $logfile ) {
|
||||
$logfile = basename( $logfile );
|
||||
|
||||
|
@ -498,7 +503,9 @@ class MainWP_Child_Back_WP_Up {
|
|||
return array( 'error' => __( 'Missing logfile.', $this->plugin_translate ) );
|
||||
}
|
||||
|
||||
$log_file = get_site_option( 'backwpup_cfg_logfolder' ) . basename( $_POST['settings']['logfile'] );
|
||||
$log_folder = get_site_option( 'backwpup_cfg_logfolder' );
|
||||
$log_folder = BackWPup_File::get_absolute_path( $log_folder );
|
||||
$log_file = $log_folder . basename( $_POST['settings']['logfile'] );
|
||||
|
||||
if ( ! is_readable( $log_file ) && ! is_readable( $log_file . '.gz' ) && ! is_readable( $log_file . '.bz2' ) ) {
|
||||
$output = __( 'Log file doesn\'t exists', $this->plugin_translate );
|
||||
|
@ -539,7 +546,11 @@ class MainWP_Child_Back_WP_Up {
|
|||
|
||||
switch ( $type ) {
|
||||
case 'logs':
|
||||
if ( ! is_dir( get_site_option( 'backwpup_cfg_logfolder' ) ) ) {
|
||||
$log_folder = get_site_option( 'backwpup_cfg_logfolder' );
|
||||
$log_folder = BackWPup_File::get_absolute_path( $log_folder );
|
||||
$log_folder = untrailingslashit( $log_folder );
|
||||
|
||||
if ( ! is_dir( $log_folder ) ) {
|
||||
return array( 'success' => 1, 'response' => $array );
|
||||
}
|
||||
update_user_option( get_current_user_id(), 'backwpuplogs_per_page', 99999999 );
|
||||
|
|
|
@ -239,10 +239,13 @@ class MainWP_Child_Branding {
|
|||
}
|
||||
|
||||
static function uploadImage( $img_url ) {
|
||||
include_once( ABSPATH . 'wp-admin/includes/file.php' ); //Contains download_url
|
||||
include_once( ABSPATH . 'wp-admin/includes/file.php' ); //Contains download_url
|
||||
global $mainWPChild;
|
||||
add_filter( 'http_request_args', array( $mainWPChild, 'http_request_reject_unsafe_urls' ), 99, 2 );
|
||||
//Download $img_url
|
||||
$temporary_file = download_url( $img_url );
|
||||
|
||||
$temporary_file = download_url( $img_url );
|
||||
remove_filter( 'http_request_args', array( $mainWPChild, 'http_request_reject_unsafe_urls' ), 99, 2 );
|
||||
|
||||
if ( is_wp_error( $temporary_file ) ) {
|
||||
throw new Exception( 'Error: ' . $temporary_file->get_error_message() );
|
||||
} else {
|
||||
|
|
|
@ -63,7 +63,7 @@ if ( class_exists( 'MainWP_WP_Stream_Connector' ) ) {
|
|||
return $links;
|
||||
}
|
||||
|
||||
public static function callback_mainwp_sucuri_scan( $data, $scan_status ) {
|
||||
public static function callback_mainwp_sucuri_scan( $data, $scan_status, $scan_data ) {
|
||||
$message = '';
|
||||
if ( 'success' === $scan_status ) {
|
||||
$message = __( 'Sucuri scan successful!', 'mainwp-child' );
|
||||
|
@ -82,7 +82,7 @@ if ( class_exists( 'MainWP_WP_Stream_Connector' ) ) {
|
|||
|
||||
self::log(
|
||||
$message,
|
||||
compact( 'scan_status', 'status', 'webtrust' ),
|
||||
compact( 'scan_status', 'status', 'webtrust', 'scan_data' ),
|
||||
0,
|
||||
array( 'mainwp_sucuri' => 'mainwp_sucuri_scan' )
|
||||
);
|
||||
|
|
|
@ -183,6 +183,9 @@ class MainWP_Child_Wordfence {
|
|||
case 'start_scan':
|
||||
$information = $this->start_scan();
|
||||
break;
|
||||
case 'kill_scan':
|
||||
$information = $this->kill_scan();
|
||||
break;
|
||||
case 'set_showhide':
|
||||
$information = $this->set_showhide();
|
||||
break;
|
||||
|
@ -345,12 +348,7 @@ class MainWP_Child_Wordfence {
|
|||
}
|
||||
|
||||
private function start_scan() {
|
||||
$information = array();
|
||||
if ( ! class_exists( 'wordfence' ) || ! class_exists( 'wfScanEngine' ) ) {
|
||||
$information['error'] = 'NO_WORDFENCE';
|
||||
|
||||
return $information;
|
||||
}
|
||||
$information = array();
|
||||
if ( wfUtils::isScanRunning() ) {
|
||||
$information['error'] = 'SCAN_RUNNING';
|
||||
|
||||
|
@ -366,6 +364,16 @@ class MainWP_Child_Wordfence {
|
|||
return $information;
|
||||
}
|
||||
|
||||
private function kill_scan() {
|
||||
wordfence::status(1, 'info', "Scan kill request received.");
|
||||
wordfence::status(10, 'info', "SUM_KILLED:A request was received to kill the previous scan.");
|
||||
wfUtils::clearScanLock(); //Clear the lock now because there may not be a scan running to pick up the kill request and clear the lock
|
||||
wfScanEngine::requestKill();
|
||||
return array(
|
||||
'ok' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
function set_showhide() {
|
||||
$hide = isset( $_POST['showhide'] ) && ( $_POST['showhide'] === 'hide' ) ? 'hide' : '';
|
||||
MainWP_Helper::update_option( 'mainwp_wordfence_hide_plugin', $hide, 'yes' );
|
||||
|
@ -459,6 +467,9 @@ class MainWP_Child_Wordfence {
|
|||
}
|
||||
|
||||
public function wfc_cron_scan() {
|
||||
if ( ! class_exists( 'wordfence' ) || ! class_exists( 'wfScanEngine' ) ) {
|
||||
return;
|
||||
}
|
||||
$this->start_scan();
|
||||
}
|
||||
|
||||
|
@ -852,14 +863,17 @@ SQL
|
|||
}
|
||||
|
||||
$sch = isset( $opts['scheduleScan'] ) ? $opts['scheduleScan'] : '';
|
||||
|
||||
if ( get_option( 'mainwp_child_wordfence_cron_time' ) !== $sch ) {
|
||||
update_option( 'mainwp_child_wordfence_cron_time', $sch );
|
||||
$sched = wp_next_scheduled( 'mainwp_child_wordfence_cron_scan' );
|
||||
if ( false !== $sched ) {
|
||||
wp_unschedule_event( $sched, 'mainwp_child_wordfence_cron_scan' );
|
||||
}
|
||||
}
|
||||
$sync_sch = ( isset( $opts['notsync_scheduleScan'] ) && $opts['notsync_scheduleScan'] ) ? false : true;
|
||||
|
||||
if ($sync_sch) {
|
||||
if ( get_option( 'mainwp_child_wordfence_cron_time' ) !== $sch ) {
|
||||
update_option( 'mainwp_child_wordfence_cron_time', $sch );
|
||||
$sched = wp_next_scheduled( 'mainwp_child_wordfence_cron_scan' );
|
||||
if ( false !== $sched ) {
|
||||
wp_unschedule_event( $sched, 'mainwp_child_wordfence_cron_scan' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['cacheType'] = wfConfig::get( 'cacheType' );
|
||||
$result['paidKeyMsg'] = false;
|
||||
|
@ -1229,7 +1243,7 @@ SQL
|
|||
if(! wfConfig::get('isPaid')){
|
||||
return array('error' => "Sorry but this feature is only available for paid customers.");
|
||||
}
|
||||
$settings = $_POST['setings'];
|
||||
$settings = $_POST['settings'];
|
||||
wfConfig::set('cbl_action', $settings['blockAction']);
|
||||
wfConfig::set('cbl_countries', $settings['codes']);
|
||||
wfConfig::set('cbl_redirURL', $settings['redirURL']);
|
||||
|
|
|
@ -84,7 +84,7 @@ if ( isset( $_GET['skeleton_keyuse_nonce_key'] ) && isset( $_GET['skeleton_keyus
|
|||
}
|
||||
|
||||
class MainWP_Child {
|
||||
public static $version = '3.4.4';
|
||||
public static $version = '3.4.5';
|
||||
private $update_version = '1.3';
|
||||
|
||||
private $callableFunctions = array(
|
||||
|
@ -1451,10 +1451,13 @@ class MainWP_Child {
|
|||
$nossl = get_option( 'mainwp_child_nossl' );
|
||||
$serverNoSsl = ( isset( $pNossl ) && 1 === (int) $pNossl );
|
||||
|
||||
if ( ( 1 === (int) $nossl ) || $serverNoSsl ) {
|
||||
$auth = ( md5( $func . $nonce . get_option( 'mainwp_child_nossl_key' ) ) === base64_decode( $signature ) );
|
||||
if ( ( 1 === (int) $nossl ) || $serverNoSsl ) {
|
||||
$auth = hash_equals( md5( $func . $nonce . get_option( 'mainwp_child_nossl_key' ) ), base64_decode( $signature ) );
|
||||
} else {
|
||||
$auth = openssl_verify( $func . $nonce, base64_decode( $signature ), base64_decode( get_option( 'mainwp_child_pubkey' ) ) );
|
||||
if ($auth !== 1) {
|
||||
$auth = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2216,8 +2219,13 @@ class MainWP_Child {
|
|||
if ( isset( $_POST['_ezin_post_category'] ) ) {
|
||||
$new_post['_ezin_post_category'] = maybe_unserialize( base64_decode( $_POST['_ezin_post_category'] ) );
|
||||
}
|
||||
|
||||
$res = MainWP_Helper::createPost( $new_post, $post_custom, $post_category, $post_featured_image, $upload_dir, $post_tags );
|
||||
|
||||
$others = array();
|
||||
if ( isset( $_POST['featured_image_data'] ) && !empty($_POST['featured_image_data'])) {
|
||||
$others['featured_image_data'] = unserialize(base64_decode( $_POST['featured_image_data'] ));
|
||||
}
|
||||
|
||||
$res = MainWP_Helper::createPost( $new_post, $post_custom, $post_category, $post_featured_image, $upload_dir, $post_tags, $others );
|
||||
|
||||
if (is_array($res) && isset($res['error'])) {
|
||||
MainWP_Helper::error( $res['error'] );
|
||||
|
@ -2503,7 +2511,7 @@ class MainWP_Child {
|
|||
if ( ! isset( $information['status'] ) && !isset($information['error']) ) {
|
||||
$information['status'] = 'SUCCESS';
|
||||
if ('update_user' === $action && isset($_POST['optimize']) && !empty($_POST['optimize'])) {
|
||||
$information['users'] = $this->get_all_users_int();
|
||||
$information['users'] = $this->get_all_users_int(500); // to fix
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3632,7 +3640,7 @@ class MainWP_Child {
|
|||
$information['themes'] = $themes;
|
||||
|
||||
if ( isset( $_POST['optimize'] ) && ( '1' === $_POST['optimize'] ) ) {
|
||||
$information['users'] = $this->get_all_users_int();
|
||||
$information['users'] = $this->get_all_users_int(500); // to fix
|
||||
}
|
||||
|
||||
if ( isset( $_POST['othersData'] ) ) {
|
||||
|
@ -3922,7 +3930,7 @@ class MainWP_Child {
|
|||
$outPost['post_type'] = $post->post_type;
|
||||
$outPost['status'] = $post->post_status;
|
||||
$outPost['title'] = $post->post_title;
|
||||
$outPost['content'] = $post->post_content;
|
||||
//$outPost['content'] = $post->post_content; // to fix overload memory
|
||||
$outPost['comment_count'] = $post->comment_count;
|
||||
// to support extract urls extension
|
||||
if ( isset( $extra['where_post_date'] ) && !empty( $extra['where_post_date'] ) ) {
|
||||
|
@ -4589,10 +4597,14 @@ class MainWP_Child {
|
|||
MainWP_Helper::write( $allusers );
|
||||
}
|
||||
|
||||
function get_all_users_int() {
|
||||
function get_all_users_int($number = false) {
|
||||
$allusers = array();
|
||||
|
||||
$new_users = get_users();
|
||||
|
||||
$params = array();
|
||||
if ($number)
|
||||
$params['number'] = $number;
|
||||
|
||||
$new_users = get_users($params);
|
||||
if ( is_array( $new_users ) ) {
|
||||
foreach ( $new_users as $new_user ) {
|
||||
$usr = array();
|
||||
|
@ -5372,7 +5384,7 @@ class MainWP_Child {
|
|||
header( 'Content-Description: File Transfer' );
|
||||
if ( MainWP_Helper::endsWith( $file, '.tar.gz' ) ) {
|
||||
header( 'Content-Type: application/x-gzip' );
|
||||
header( "Content-Encoding: gzip'" );
|
||||
header( "Content-Encoding: gzip" );
|
||||
} else {
|
||||
header( 'Content-Type: application/octet-stream' );
|
||||
}
|
||||
|
|
|
@ -106,7 +106,8 @@ class MainWP_Client_Report {
|
|||
}
|
||||
|
||||
public function save_sucuri_stream() {
|
||||
do_action( 'mainwp_sucuri_scan', $_POST['result'], $_POST['scan_status'] );
|
||||
$scan_data = isset($_POST['scan_data']) ? $_POST['scan_data'] : '';
|
||||
do_action( 'mainwp_sucuri_scan', $_POST['result'], $_POST['scan_status'], $scan_data );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -655,7 +656,31 @@ class MainWP_Client_Report {
|
|||
case 'status': // sucuri cases
|
||||
case 'webtrust':
|
||||
if ( 'mainwp_sucuri' === $context ) {
|
||||
$token_values[ $token ] = $this->get_stream_meta_data( $record, $data );
|
||||
|
||||
$scan_data = $this->get_stream_meta_data( $record, 'scan_data' );
|
||||
if (!empty($scan_data)) {
|
||||
$scan_data = maybe_unserialize( base64_decode( $scan_data ) );
|
||||
if ( is_array( $scan_data ) ) {
|
||||
|
||||
$blacklisted = $scan_data['blacklisted'];
|
||||
$malware_exists = $scan_data['malware_exists'];
|
||||
|
||||
$status = array();
|
||||
if ( $blacklisted ) {
|
||||
$status[] = __( 'Site Blacklisted', 'mainwp-child' ); }
|
||||
if ( $malware_exists ) {
|
||||
$status[] = __( 'Site With Warnings', 'mainwp-child' ); }
|
||||
|
||||
if ($data == 'status') {
|
||||
$token_values[$token] = count( $status ) > 0 ? implode( ', ', $status ) : __( 'Verified Clear', 'mainwp-child' );
|
||||
} else if ($data == 'webtrust') {
|
||||
$token_values[$token] = $blacklisted ? __( 'Site Blacklisted', 'mainwp-child' ) : __( 'Trusted', 'mainwp-child' );
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$token_values[ $token ] = $this->get_stream_meta_data( $record, $data );
|
||||
}
|
||||
} else {
|
||||
$token_values[ $token ] = $value;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ class MainWP_Clone {
|
|||
id="mainwp-child_displayby_sitename"><?php esc_html_e( 'Site Name', 'mainwp-child' ); ?></a><a
|
||||
class="mainwp-child_action right" href="#"
|
||||
id="mainwp-child_displayby_url"><?php esc_html_e( 'URL', 'mainwp-child' ); ?></a></div>
|
||||
<h2 class="hndle"><?php esc_html_e( 'Clone Options', 'mainwp-child' ); ?></h2>
|
||||
<h2 class="hndle"><?php esc_html_e( 'Select Source for clone', 'mainwp-child' ); ?></h2>
|
||||
<div class="inside">
|
||||
<div id="mainwp-child_clonesite_select_site">
|
||||
<?php
|
||||
|
@ -196,8 +196,10 @@ class MainWP_Clone {
|
|||
}
|
||||
?>
|
||||
</div>
|
||||
<p><?php _e("The site selected above will replace this site's files and database", 'mainwp-child'); ?></p>
|
||||
</div>
|
||||
<div class="mainwp-child_clonebutton_container"><?php if ( ! $error ) { ?><a href="#"
|
||||
<div class="mainwp-child_clonebutton_container"><?php if ( ! $error ) { ?>
|
||||
<a href="#"
|
||||
id="mainwp-child_clonebutton"
|
||||
class="button-primary"><?php esc_html_e( 'Clone website', 'mainwp-child' ); ?></a><?php } ?>
|
||||
</div>
|
||||
|
|
|
@ -209,7 +209,7 @@ class MainWP_Helper {
|
|||
return array( 'path' => $full_file_name );
|
||||
}
|
||||
|
||||
static function createPost( $new_post, $post_custom, $post_category, $post_featured_image, $upload_dir, $post_tags ) {
|
||||
static function createPost( $new_post, $post_custom, $post_category, $post_featured_image, $upload_dir, $post_tags, $others = array() ) {
|
||||
global $current_user;
|
||||
$wprocket_fields = array( 'lazyload', 'lazyload_iframes', 'minify_html', 'minify_css', 'minify_js', 'cdn' );
|
||||
$wprocket_activated = false;
|
||||
|
@ -591,6 +591,16 @@ class MainWP_Helper {
|
|||
if ( null !== $upload ) {
|
||||
update_post_meta( $new_post_id, '_thumbnail_id', $upload['id'] ); //Add the thumbnail to the post!
|
||||
$featured_image_exist = true;
|
||||
if (isset($others['featured_image_data'])) {
|
||||
$_image_data = $others['featured_image_data'];
|
||||
update_post_meta( $upload['id'], '_wp_attachment_image_alt', $_image_data['alt'] );
|
||||
wp_update_post( array( 'ID' => $upload['id'],
|
||||
'post_excerpt' => $_image_data['caption'],
|
||||
'post_content' => $_image_data['description'],
|
||||
'post_title' => $_image_data['title']
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
|
||||
|
@ -1271,23 +1281,29 @@ class MainWP_Helper {
|
|||
* Credit to the : wp-filters-extras
|
||||
*/
|
||||
|
||||
public static function remove_filters_with_method_name( $hook_name = '', $method_name = '', $priority = 0 ) {
|
||||
global $wp_filter;
|
||||
// Take only filters on right hook name and priority
|
||||
if ( ! isset( $wp_filter[ $hook_name ] ) || ! isset( $wp_filter[ $hook_name ][ $priority ] ) || ! is_array( $wp_filter[ $hook_name ][ $priority ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loop on filters registered
|
||||
foreach ( (array) $wp_filter[ $hook_name ][ $priority ] as $unique_id => $filter_array ) {
|
||||
// Test if filter is an array ! (always for class/method)
|
||||
if ( isset( $filter_array['function'] ) && $filter_array['function'] === $method_name ) {
|
||||
unset( $wp_filter[ $hook_name ][ $priority ][ $unique_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
function remove_filters_with_method_name( $hook_name = '', $method_name = '', $priority = 0 ) {
|
||||
global $wp_filter;
|
||||
// Take only filters on right hook name and priority
|
||||
if ( ! isset( $wp_filter[ $hook_name ][ $priority ] ) || ! is_array( $wp_filter[ $hook_name ][ $priority ] ) ) {
|
||||
return false;
|
||||
}
|
||||
// Loop on filters registered
|
||||
foreach ( (array) $wp_filter[ $hook_name ][ $priority ] as $unique_id => $filter_array ) {
|
||||
// Test if filter is an array ! (always for class/method)
|
||||
if ( isset( $filter_array['function'] ) && is_array( $filter_array['function'] ) ) {
|
||||
// Test if object is a class and method is equal to param !
|
||||
if ( is_object( $filter_array['function'][0] ) && get_class( $filter_array['function'][0] ) && $filter_array['function'][1] == $method_name ) {
|
||||
// Test for WordPress >= 4.7 WP_Hook class
|
||||
if ( is_a( $wp_filter[ $hook_name ], 'WP_Hook' ) ) {
|
||||
unset( $wp_filter[ $hook_name ]->callbacks[ $priority ][ $unique_id ] );
|
||||
} else {
|
||||
unset( $wp_filter[ $hook_name ][ $priority ][ $unique_id ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function sanitize_filename( $filename ) {
|
||||
if (!function_exists('mb_ereg_replace')) return sanitize_file_name($filename);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
/*
|
||||
Plugin Name: MainWP Child
|
||||
Plugin URI: https://mainwp.com/
|
||||
Description: Provides a secure connection between your MainWP Dashboard and your WordPress sites. MainWP allows you to manage WP sites from one central location. Plugin documentation and options can be found here http://docs.mainwp.com
|
||||
Description: Provides a secure connection between your MainWP Dashboard and your WordPress sites. MainWP allows you to manage WP sites from one central location. Plugin documentation and options can be found here https://mainwp.com/help/
|
||||
Author: MainWP
|
||||
Author URI: https://mainwp.com
|
||||
Text Domain: mainwp-child
|
||||
Version: 3.4.4
|
||||
Version: 3.4.5
|
||||
*/
|
||||
if ( ( isset( $_REQUEST['heatmap'] ) && '1' === $_REQUEST['heatmap'] ) || ( isset( $_REQUEST['mainwpsignature'] ) && ( ! empty( $_REQUEST['mainwpsignature'] ) ) ) ) {
|
||||
header( 'X-Frame-Options: ALLOWALL' );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue