diff --git a/html/admin_menu.html b/html/admin_menu.html new file mode 100644 index 0000000..f337e4f --- /dev/null +++ b/html/admin_menu.html @@ -0,0 +1,50 @@ + + + +

email a la creation de compte

+ +
+ + +
+ + +
+ /> + +
+ +
+ /> + +
+ + + + diff --git a/php/classes/plgntls_class.php b/php/classes/plgntls_class.php new file mode 100644 index 0000000..e0bc464 --- /dev/null +++ b/php/classes/plgntls_class.php @@ -0,0 +1,1369 @@ + 'path/to/style2.css', // -> depends on style1.css +* 'http://my_url1.com', // +* 'script1_js' => 'path/to/script2.js', // -> depends on script1.js +* 'path/to/file1.html', // | will be returned +* 'path/to/file2.html', // -> | as expanded html +* 'path/to/file3.html', // | in the order included +* 'handle_name' => 'path/to/script3.js', // -> depends on the script with handle 'handle_name' +* array( // +* 'path/to/script4.js', // | add a script with attributes +* 'attribute_1' => 'value_1', // -> | first element of array must be +* 'attribute_2' => 'value_2', // | the script, without explicit key +* ), // +* array( 'js' => 'var_js' ), // -> add inline js, only first element will be used +* array( 'css' => 'var_css' ), // -> add inline css, only first element will be used +* ), +* compact( // these variables are added to html and js files +* 'var_1', // - in html files you can access them directly +* 'var_2', // - in js files they are properties of object PLGNTLS_data +* 'var_3', // like PLGNTLS_data.var_1; +* ) +* ); +* +*/ + +class Plgntls_xtx { + /* + * 0 : dont output + * 1 : output only level 1 - not always on top of function, output default, only prevent if specifically level 2 + * typical for early hooks like 'init' or 'wp', where there is a first check to see if you should 'enter' in this function, level 1 will be present after thoses checks + * 2 : output everything + * + */ + private static $_DEBUG_INFOS = 0; + + private static $_instance_count = 0; + private static $_adding_count = 0; + private static $_init_count = 0; + + private $_first_script = null; + private $_first_style = null; + private $_js_dependencies = array(); + private $_css_dependencies = array(); + private $_scripts_attributes = array(); + + private static $_prefix = null; + private static $_options_action = null; + private static $_options_list = null; + + /* + */ + public function __construct() { + ++self::$_instance_count; + self::init_class(); + } + + + + + + + + + public static function init_class() { + if (self::$_init_count > 0) { + return; + } + ++self::$_init_count; + + self::$_prefix = strtoupper(__CLASS__); + + /* + * 'set paths and urls' init + * + */ + self::_init_root_dir(); + + + /* + * 'debug logs' init + * + */ + + + /* + * 'add to front' init + * + */ + + + /* + * 'add menu' init + * + */ + add_filter("plugin_action_links_".self::$_plugin_dir."/".self::$_main_file, function($links) { + return self::_add_link_to_plugin($links); + }); + + + /* + * 'handle options' init + * + */ + self::$_options_action = 'action_for_admin_post_options_'.self::$_prefix; + self::$_options_list = 'list_of_options_for_this_plugin_'.self::$_prefix; + if (false === get_option(self::$_options_list)) { + add_option(self::$_options_list, '', '', 'no'); + } + add_action('admin_post_'.self::$_options_action, function() { + self::_handle_admin_post_option(); + }); + } + + + + + + + + + +/* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* ( +* ACTIONS FUNCTIONS +* +* 1. set paths and urls +* 2. debug logs +* 3. add to front +* 4. add menu +* 5. handle options +* +* END{ 5{ 4{ 3{ 2{ 1{ +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +*/ + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * 1 * + * * * * * * * + *})( set paths and urls * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + private static $_plugin_dir_path; + private static $_plugin_dir; + private static $_file_dir_path; + private static $_file_name; + private static $_main_file; + private static $_root_path; + private static $_root_url; + + + /* + * ex: + * /home/www-data/cipf_plugin/php/test/test2/test3/test.php + * _plugin_dir_path /home/www-data + * _plugin_dir cipf_plugin + * _file_dir_path php/test/test2/test3 + * _file_name test.php + * _root_path /home/www-data/cipf_plugin/ + * _main_file main_file,php + * + * /home/www-data/cipf_plugin/test.php + * _plugin_dir_path /home/www-data + * _plugin_dir cipf_plugin + * _file_dir_path '' + * _file_name test.php + * _root_path /home/www-data/cipf_plugin/ + * _main_file main_file,php + * + */ + private static function _init_root_dir() { + if (isset(self::$_plugin_dir, self::$_file_name, self::$_file_dir_path, self::$_plugin_dir_path)) + return ; + /* + * it uses exploded_path_path by removing data from the array + * so order is important ! + * plugin_dir / path / to / file.php + * exploded [plugin_dir, path, to, file.php] + * plugin_dir plugin_dir [path, to, file.php] + * file_name [path, to] file.php + * file_dir_name path / to + */ + $exploded_plugin_path = explode('/', plugin_basename( __FILE__ )); + + self::$_plugin_dir = array_shift($exploded_plugin_path); + self::$_file_name = array_pop($exploded_plugin_path); + self::$_file_dir_path = implode('/', $exploded_plugin_path); + self::$_plugin_dir_path = str_replace('/'.plugin_basename(__DIR__).'/', '', plugin_dir_path(__FILE__)); + + // https://wordpress.stackexchange.com/questions/19900/how-to-get-main-plugin-theme-file + $plugin_data = get_plugins("/".self::$_plugin_dir); + self::$_main_file = array_keys($plugin_data)[0]; + + self::$_root_path = self::$_plugin_dir_path.'/'.self::$_plugin_dir.'/'; + self::$_root_url = plugins_url(self::$_plugin_dir.'/'); + } + public static function root_path() { + self::init_class(); + return(self::$_root_path); + } + public static function root_url() { + self::init_class(); + return(self::$_root_url); + } + + + + + + + + + + + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * 2 * + * * * * * * * + *})( debugs logs * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + public static function debug_infos($level = null) { + if (self::$_DEBUG_INFOS === 0) { + return; + } + else if (self::$_DEBUG_INFOS === 1 && $level === 2) { + return; + } + $trace = debug_backtrace(); + $function = $trace[1]['function']; + $file = $trace[0]['file']; + $line = $trace[0]['line']; + } + + + + + + + + + + + + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * 3 * + * * * * * * * + *})( add to front * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + + + public static function add_to_front($srcs_arr = array(), $vars = array()) { + $instance = new self(); + return $instance->_add_to_front($srcs_arr, $vars); + } + + private function _add_to_front($srcs_arr, $vars) { + /* + * even if the function is called with no srcs + * add fetch, because it can be used just for that + */ + if (self::$_adding_count === 0) { + $this->_add_fetch($srcs_arr, $vars); + } + + $srcs = array(); + foreach($srcs_arr as $src_key => $src_value) { + $init = $this->_init_src($src_key, $src_value); + if ($init !== null) + $srcs[] = $init; + } + if (!is_null($srcs_arr)) { + $this->_add_srcs_to_front($srcs); + } + + if (!is_null($vars)) { + $this->_add_vars_to_front($vars); + } + $add_html = $this->_create_html($srcs, $vars); + self::$_adding_count += 1; + return $add_html; + } + + + /* + * for fetch, we add the script that contains the fetch function + * it's an inline script, but is made globally available by adding it to window + * + */ + private function _add_fetch(&$srcs_arr, &$vars) { + // add fetch script at end of scripts list + // it will try to be added at the first script anyway, + // but for that, the scripts must be already enqueued, + // hence adding it at the end of the array + $srcs_arr[] = array('js'=>$this->_fetch_script()); + // for the custom endpoints in rest api to work + // they need to have a nonce named 'wp_rest' + // see : https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/ + $fetch_nonce = array("fetch_nonce" => wp_create_nonce('wp_rest')); + $fetch_url = array("fetch_url" => get_site_url() . "/wp-json"); + $vars = array_merge($vars, $fetch_nonce); + $vars = array_merge($vars, $fetch_url); + } + private function _fetch_script() { + return " + if (typeof window !== 'undefined') { + window.PLGNTLS_fetch = function PLGNTLS_fetch(url, options = {}) { + url = PLGNTLS_data.fetch_url + url; + + options.headers = options.headers || {}; + options.headers['X-WP-Nonce'] = PLGNTLS_data.fetch_nonce; + + return fetch(url, options); + } + } + "; + } + + + /* + * @param two arguments : + * 1. html files to include in front + * - can be a string of 1 filename + * - or an array of strings of filenames + * ( https://stackoverflow.com/q/4747876/9497573 ) + * - it's probably better to only add 1 file, and let it include other files + * 2. list of variables to make available to this files + * - in the form of key => val + * - recommanded to do it with compact() + * ex: _create_html( "file.html", compact("var1","var2",) ); + * ex: _create_html( array("file1.html", "file2.html"), array("var1"=>"value") ); + * @return a string of html code + * + */ + private function _create_html($files = null, $vars = null) { + if (is_null($files)) + return null; + $plgn_dir = $this->root_path(); + if (!is_null($vars)) + extract($vars); + + // using ob_start() and ob_get_clean() + // allows to have php expansion inside the html loaded + // in opposition to the method file_get_contents() + // https://stackoverflow.com/a/4402045/9497573 + ob_start(); + foreach($files as $file) { + if ($file->ext === 'html') + include($file->path); + } + return ob_get_clean(); + } + + /* + * pass variables to js front as global variables + * @param array : list of key => value + * with the key being name of the variable, like this : + * 'my_var' => 'value', + * simpler way to do it is to use compact when calling the function : + * add_var_to_front(compact("var1", "var2", "var3")); + * @param string (optionnal) : name of first embended script that need these variables + * (it will be available to this script and all followings) + * this name is the filename + "_" + extension : + * init.js -> init_js + */ + private function _add_vars_to_front($vars_arr) { + if (is_null($vars_arr)) + return ; + + $object_name = self::$_prefix . "_data"; + $vars_json = json_encode($vars_arr); + // note : we need to use 'var' instead of 'let' or 'const', + // because their scope is restricted to the if statement + $front = " + if (typeof $object_name === 'undefined') { + var $object_name = $vars_json; + } + else { + Object.assign($object_name, $vars_json); + } + "; + + $handle = $this->_check_inline_handles('js'); + if (!is_null($handle)) { + wp_add_inline_script($handle, $front, 'before'); + } + else { + // in last ressort, but bad + echo ''; + } + } + + /* + * @param array : list of files : + * - with their path from root of their type of file (ex: from js/ to .js files) + * - and with their extension + * @param boolean + * - to add ajax script and variables + * - default to true + */ + private function _add_srcs_to_front($srcs_arr) { + //wp_enqueue_script(, /url/to/script, [depends on], version, defer? ); + //wp_enqueue_style( , /url/to/script, [depends on], version, media ); + + $previous_css_basename = ''; + $previous_js_basename = ''; + foreach ($srcs_arr as $src) { + if ($src->inline === "js") { + $this->_add_inline_script($src); + } + else if ($src->inline === "css") { + $this->_add_inline_style($src); + } + else if (in_array($src->ext, array("js", "url"))) { + $this->_add_script($src, $previous_js_basename); + $previous_js_basename = $src->handle; + } + else if ($src->ext === "css") { + $this->_add_style($src, $previous_css_basename); + $previous_css_basename = $src->handle; + } + } + + // https://developer.wordpress.org/reference/hooks/wp_script_attributes/ + // https://wordpress.stackexchange.com/questions/66843/attach-a-private-function-at-a-hook + add_filter( 'wp_script_attributes', fn($attr)=>$this->_add_attributes_to_script($attr), 10, 1 ); + + //uncomment to print all enqueued files, can be usefull + /* + global $wp_scripts; + error_log("wp_scripts->queue:"); + error_log(json_encode($wp_scripts->queue)); + global $wp_styles; + error_log("wp_styles->queue:"); + error_log(json_encode($wp_styles->queue)); + */ + } + private function _add_script($script, $previous_js_basename) { + if (is_null($this->_first_script)) { + $this->_first_script = $script->handle; + } + $depends_on = $this->_check_dependencies($script, $previous_js_basename); + if ($depends_on !== null) { + wp_enqueue_script( $script->handle, $script->url, $depends_on, $script->version, true); + } + } + private function _add_style($style, $previous_css_basename) { + if (is_null($this->_first_style)) { + $this->_first_style = $style->handle; + } + $depends_on = $this->_check_dependencies($style, $previous_css_basename); + if ($depends_on !== null) { + wp_enqueue_style( $style->handle, $style->url, $depends_on, $style->version, ''); + } + } + private function _add_inline_script($src) { + $handle = $this->_check_inline_handles('js', $src); + if (!is_null($handle)) { + wp_add_inline_script($handle, $src->src, 'before'); + } + else { + // in last ressort, only add the script inline, it's not ideal, + // but the only situation where it should not work is if another script is loaded before + // and it should not be the case otherwise the handle would not have returned true + echo ''; + } + } + private function _add_inline_style($src) { + $handle = $this->_check_inline_handles('css', $src); + if (!is_null($handle)) { + wp_add_inline_style($handle, $src->src); + } + else { + // in last ressort, cf script notes above + echo ''; + } + } + + private function _add_attributes_to_script($attr) { + if (empty($attr['id'])) { + return $attr; + } + $handle = $attr['id']; + if (isset($this->_scripts_attributes[$handle])) { + $script = $this->_scripts_attributes[$handle]; + } + else { + return $attr; + } + + foreach($script as $key => $value){ + $attr[$key] = $value; + } + unset($this->_scripts_attributes[$handle]); + + return $attr; + } + + private function _check_dependencies(&$src, $previous_basename) { + if (in_array($src->ext, array("js", "url"))) { + global $wp_scripts; + $already_enqueued = array_search($src->handle, $wp_scripts->queue); + if ($already_enqueued !== false) { + return null; + } + } + else if ($src->ext === "css") { + global $wp_styles; + $already_enqueued = array_search($src->handle, $wp_styles->queue); + if ($already_enqueued !== false) { + return null; + } + } + $depends_on = array(); + if (isset($src->depends) && $src->depends !== '') { + $depends_on[] = $src->depends; + } + if (isset($previous_basename) && $previous_basename !== '') { + $depends_on[] = $previous_basename; + } + + return $depends_on; + } + + private function _check_inline_handles($type = null, $src = null) { + /* + * first, try to simply get the explicit dependency + * + */ + $handle = null;; + if (!is_null($src)){ + $handle = $src->depends; + } + if (!empty($handle) && !is_null($handle)) { + return $handle; + } + + /* + * second, try to get the first enqueued src of this call to 'add_to_front()' + * and make it the dependency + * default to js + * + */ + if ($type === "js" || is_null($type)) { + $handle = $this->_first_script; + } + else if ($type === "css") { + $handle = $this->_first_style; + } + if (!empty($handle) && !is_null($handle)) { + return $handle; + } + + /* + * third, try to get the last enqueued src of the page + * https://www.php.net/manual/en/function.array-key-last.php + * + */ + if ($type === "js" || is_null($type)) { + global $wp_scripts; + $array = $wp_scripts->queue; + } + else if ($type === "css") { + global $wp_styles; + $array = $wp_styles->queue; + } + $last_key = array_key_last($array); + if (!is_null($last_key)) { + return $array[$last_key]; + } + + /* + * didn't find any handle to add the src to it + * + */ + return null; + } + + /* + * @param string : name of the file, with its path from its extension directory + * - from js/ root for .js files + * - from css/ root for .css files + * @return object / null : + * - null if file is not valid + * - or an object with all the necessary infos : + * 0. src : array("name.js", ...) -> "name.js" + * "name.js" -> "name.js" + * 1. ext : name.js -> "js" + * 2. basename : name.js -> "name" + * 3. handle : name.js -> "name_js" + * 4. url : url to file in wordpress + * 5. path : path to file in server + * 6. version : used to avoid browser caching + * 7. depends : string if depends on a handle, or '' + * 8. attr : associative array of html attribute like 'type'=>'module' + * 9. inline : array("js" => "name.js") -> js + * array("css" => "name.css") -> css + */ + private function _init_src($key, $value) { + if (empty($value)) + return null; + $src = (object)[ + 'src' => null, + 'ext' => null, + 'basename' => null, + 'handle' => null, + 'url' => null, + 'path' => null, + 'version' => null, + 'depends' => null, + 'attr' => null, + 'inline' => null, + ]; + + // 7. depends + if (is_string($key)) + $src->depends = $key; + + // 0. src + // 8. attr + // 9. inline + // first element of array is used, so must not be empty + // value => ['path/to/file', 'key1'=>'value1', 'key2'=>'value2'] + // src => 'path/to/file' + // attr => ['key1'=>'value1', 'key2'=>'value2'] + if (is_array($value)){ + $first_key = array_keys($value)[0]; + if (empty($value[$first_key])) + return null; + if ($first_key === 0) { // is a script file or url with attributes + $src->src = array_shift($value); + $src->attr = $value; + } + else if ($first_key === "js" || $first_key === "css") { // is an inline code + $src->src = $value[$first_key]; + $src->inline = $first_key; + return $src; // inline only needs 'depends', 'src' and 'inline' + } + } + else { + $src->src = $value; + $src->attr = null; + } + + // 1. ext + if(filter_var($src->src, FILTER_VALIDATE_URL)) + $src->ext = "url"; + else + $src->ext = pathinfo($src->src, PATHINFO_EXTENSION); + if (! in_array($src->ext, array("js", "css", "html", "url"))) + return null; + + // 2. basename + // 3. handle + // basename handle + // https://www.url.com/route -> www.url.com/route -> www_url_com_route + // path/to/script.js -> script.js -> script_js + if ($src->ext === "url") { + $url = parse_url($src->src); + $src->basename = $url['host'] . $url['path']; + } + else + $src->basename = pathinfo($src->src, PATHINFO_BASENAME); + $src->handle = "PLGNTLS_" . str_replace(array('.', '/'), "_", $src->basename); + + // 4. url + // 5. path + // 6. version + if ($src->ext === "url") { + $src->url = $src->src; + $src->path = null; + $src->version = null; + } + else { + $src->url = $this->root_url().$src->src; + $src->path = $this->root_path().$src->src; + $src->version = date("ymd-Gis", filemtime($src->path)); + } + + // if ext is 'js' or 'url' and attr is not empty + // also add to global variable to access in 'wp_script_attributes' filter + if ($src->ext === 'js' || $src->ext === 'url') { + if ($src->attr !== null) { + $this->_scripts_attributes[$src->handle.'-js'] = $src->attr; + } + } + + return $src; + } + + + + + + + + + + + + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * 4 * + * * * * * * * + *})( add menu * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + const OPTION_TOGGLE_MENU = [ + '_name'=>'toggle_admin_menu_option_xtxpatch', + '_default'=>'hide', + 'show'=>'show', + 'hide'=>'hide', + ]; + + + + /* + * options is the options needed to set a new menu + * see _create_menu below for more informations + * + */ + public static function add_menu($menu_options) { + /* + * will init the filter for the toggle + * + */ + self::init_class(); + + if (empty($menu_options)) { + return; + } + + /* + * if menu_options is only a string, we assume it is the callback + * + */ + if (is_string($menu_options)) { + $menu_options = array('callback'=>$menu_options); + } + + self::_create_menu($menu_options); + } + + /* + * triggered by filter "admin_menu" + * + * page_title -> (optional, default 'name') + * name -> (optional, default _plugin_dir) + * capability -> (optional, default 'manage_options') + * slug -> (optional, default 'name') + * callback -> required + * toggle -> (optional, default true) + * + */ + private static function _create_menu($menu_options) { + if (!isset($menu_options['name'])) { + $menu_options['name'] = self::$_plugin_dir; + } + $default = array( + 'page_title'=> $menu_options['name'], + 'name' => $menu_options['name'], + 'capability'=> 'manage_options', + 'slug' => $menu_options['name'], + 'callback' => $menu_options['callback'], + 'toggle' => true, + ); + foreach ($default as $key => $value) { + if (!isset($menu_options[$key])) { + $menu_options[$key] = $value; + } + } + + if (false === $menu_options['toggle']) { + add_menu_page($menu_options['page_title'], $menu_options['name'], $menu_options['capability'], $menu_options['slug'], $menu_options['callback']); + } + else { + self::_toggle_menu($menu_options['page_title'], $menu_options['name'], $menu_options['capability'], $menu_options['slug'], $menu_options['callback']); + } + } + private static function _toggle_menu($menu_page_title, $menu_title, $menu_capability, $menu_slug, $menu_callback) { + $toggle_menu = self::OPTION_TOGGLE_MENU; + + $toggle = self::get_option_safe($toggle_menu); + + if ($toggle === $toggle_menu['hide']) { + remove_menu_page($menu_slug); + } + else if ($toggle === $toggle_menu['show']) { + add_menu_page($menu_page_title, $menu_title, $menu_capability, $menu_slug, $menu_callback); + } + } + + /* + * add link under the plugin in the plugins admin page + * triggered by filter “plugin_action_links_{$plugin_file}” + * + */ + private static function _add_link_to_plugin($links) { + $option_toggle = self::OPTION_TOGGLE_MENU; + + $toggle = self::get_option_safe($option_toggle); + if (false === $toggle) { + return $links; + } + if (!in_array($toggle, ['hide', 'show'])) { + return $links; + } + $state = $toggle === 'show' ? 'hide' : 'show'; + + $link = "".$state." menu"; + $links[] = $link; + + return $links; + } + + + + + /* + * callback of the option toggle_menu + * handle the toggle menu when url is reached + * + */ + public static function toggle_plugin_menu($request, $option_name, $option_value) { + if ($request[$option_name] === 'show') { + update_option($option_name, 'show'); + } + else if ($request[$option_name] === 'hide') { + update_option($option_name, 'hide'); + } + } + + + + + + + + + + + + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * 5 * + * * * * * * * + *})( handle options * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + + + public static function get_option_safe($option) { + /* + * first init option, in case it was not already + * it will returns the option name or false + * it also works if the option is the name directly + * + */ + $option_name = self::_init_option($option); + if (false === $option_name) { + return; + } + + /* + * get option + * try to unserialize it + * if unserialization failed, it means it was not serialized + * - unserialize returns false if cannot unserialize, + * - which can also means it unserialized successfully the value 'false' + * - so checking for this special case with the serialized value of 'false' + * + */ + $option_data = get_option($option_name); + if (!is_string($option_data)) { + $ret_option = $option_data; + } + else { + $false_serialized = serialize(false); + $option_unserialized = @unserialize($option_data); + if ($option_unserialized === false && $option_data != $false_serialized) { + $ret_option = $option_data; + } + else { + $ret_option = $option_unserialized; + } + } + + return $ret_option; + } + + public static function update_option_safe($option, $option_data) { + /* + * first init option, in case it was not already + * it will returns the option name or false + * it also works if the option is the name directly + * + */ + $option_name = self::_init_option($option); + if (false === $option_name) { + return; + } + + if (!is_string($option_data)) { + $option_data = serialize($option_data); + } + update_option($option_name, $option_data); + } + + public static function get_option_link_href($option_name) { + $option_data = self::_get_option_data($option_name); + if (false === $option_data) { + return null; + } + $href = admin_url('admin-post.php'); + $href .= "?action=" . $option_data['_action']; + $href .= "&" . $option_data['_nonce_name'] . "=" . wp_create_nonce($option_data['_nonce_action']); + $href .= "&option_name=" . $option_data['_name']; + + return $href; + } + + /* + * this replaces the form opening tag
+ * it creates this tag with the appropriate action, + * and add 3 hidden inputs fields for form action and nonce + * + */ + public static function open_form_option($option_name, $method = "post") { + $option_data = self::_get_option_data($option_name); + if (false === $option_data) { + return null; + } + ob_start(); + echo ''; + echo ''; + echo ''; + wp_nonce_field($option_data["_nonce_action"], $option_data["_nonce_name"], true, true); + return ob_get_clean(); + } + + + private static function _get_option_data($option_name) { + if (!is_string($option_name)) { + return false; + } + + $options_serialized = get_option(self::$_options_list); + $options_unserialized = unserialize($options_serialized); + if (!isset($options_unserialized[$option_name])) { + return false; + } + + return $options_unserialized[$option_name]; + } + + /* + * a valid option_data must contains '_name' and '_default' at least + * if the option was retrieve in the database, + * there is no need to check it again : special field _db = null + * + * returns the option name if ok, or false + * + */ + private static function _init_option($option) { + if (is_string($option)) { + if (self::_is_in_list_option($option)) { + return $option; + } + else { + return false; + } + } + + if (!isset($option['_name'])) { + return false; + } + if (!isset($option['_default'])) { + return false; + } + if (isset($option['_db'])) { + return $option['_name']; + } + + /* + * if wp option does not already exists, just add it + * otherwise, get it and check the values + * + */ + $name = $option['_name']; + if (false === get_option($name)) { + add_option($name, '', '', 'no'); + self::update_option_safe($name, $option['_default']); + } + + /* + * checks all the default fields : + * - _action, default 'action_for_admin_post_options_'.self::_prefix + * if you use another action, it will not trigger the class handler function + * - _nonce_action + * - _nonce_name + * - _callback, default + * + */ + $default_option = array( + '_action'=>self::$_options_action, + '_nonce_action'=>'nonce_action_'.$name, + '_nonce_name'=>'nonce_name_'.$name, + '_callback'=>__CLASS__.'::default_handle_admin_post_option', + ); + foreach ($default_option as $key => $value) { + if (!isset($option[$key])) { + $option[$key] = $value; + } + } + + /* + * override saved option with new one + * or we could compare each fields and update them if needed + * - but is it not more heavy in computing actions ? + * also add the field '_db' equal to anything (null by default) + * to mark it as coming from this database + * and differentiate it from new options + * + */ + $options_serialized = get_option(self::$_options_list); + $options_unserialized = unserialize($options_serialized); + $option['_db'] = null; + $options_unserialized[$name] = $option; + update_option(self::$_options_list, serialize($options_unserialized), '', 'no'); + + return $name; + } + + private static function _is_in_list_option($option_name) { + $options_serialized = get_option(self::$_options_list); + $options_unserialized = unserialize($options_serialized); + return isset($options_unserialized[$option_name]); + } + + + private static function _handle_admin_post_option() { + if (!isset($_REQUEST)) { + return; + } + $request = $_REQUEST; + + /* + * get values from the request, and unset them : + * - name of the option -> to get the option_data + * then get the option data : + * - nonce infos -> to check the nonce (nonce_name and nonce_action) + * - callback + * with nonce name, get the nonce from the request, and check it + * + */ + unset($request['action']); + $option_name = $request['option_name']; + unset($request['option_name']); + // option data : + $option_data = self::_get_option_data($option_name); + if (!isset( + $option_data['_nonce_name'], + $option_data['_nonce_action'], + $option_data['_callback']) + ) { + error_log("there has been a problem, the option data don't contains the necessary informations (request: " . json_encode($_REQUEST) . ")(option data: " . json_encode($option_data)); + self::redirect_menu_referer(); + exit; + } + $nonce_name = $option_data['_nonce_name']; + $nonce_action = $option_data['_nonce_action']; + $nonce_callback = $option_data['_callback']; + // check the nonce + $nonce = $request[$nonce_name]; + unset($request[$nonce_name]); + if (!wp_verify_nonce($nonce, $nonce_action)) { + self::redirect_menu_referer(); + exit; + } + + /* + * if nonce passed, call the callback + * - with the remaining of the request + * - the option name + * - the option default value + * - and the option default data to have the fields + * + $nonce_callback($request, $option_name, $option_data['_default']); + */ + $option_value = self::get_option_safe($option_name); + if (false === $option_value) { + self::redirect_menu_referer(); + exit; + } + $nonce_callback($request, $option_name, $option_value, $option_data['_default']); + + /* + * then redirects + * + */ + self::redirect_menu_referer(); + exit; + } + + public static function default_handle_admin_post_option($request, $option_name, $option_data, $option_default) { + /* + * in case the option only contains one value, just update it + * + */ + if (is_string($option_data)) { + self::update_option_safe($option_name, $request[$option_name]); + return; + } + + /* + * else, assuming it's a 1 dimension array + * update each values + * + */ + foreach ($option_default as $key => $value) { + if (isset($request[$key])) { + $option_data[$key] = $request[$key]; + } + } + self::update_option_safe($option_name, $option_data); + } + + + public static function redirect_menu_referer() { + if (wp_get_referer()) { + wp_safe_redirect(wp_get_referer()); + exit; + } + else { + wp_safe_redirect(admin_url()); + exit; + } + } + + + + + + + + + /* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + *}) end + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ +} + + +?> diff --git a/php/classes/xtxpatch_class.php b/php/classes/xtxpatch_class.php new file mode 100644 index 0000000..760f060 --- /dev/null +++ b/php/classes/xtxpatch_class.php @@ -0,0 +1,36 @@ +'define_email_at_register_xtxpatch', + '_callback'=>__NAMESPACE__.'\define_register_email', + '_default'=>[ + 'email'=> +'Bonjour, +Vous venez de créer un compte sur le site carteprof.org avec l’identifiant : $$user_login$$ +La FIPF', + 'is_email_prof'=>true, + 'is_email_partner'=>true, + ], + ]; +} + +?> diff --git a/php/menu/admin_menu.php b/php/menu/admin_menu.php new file mode 100644 index 0000000..45449d1 --- /dev/null +++ b/php/menu/admin_menu.php @@ -0,0 +1,137 @@ + $email, + 'is_email_prof' => $is_email_prof, + 'is_email_partner' => $is_email_partner, + ); + \Plgntls_xtx::update_option_safe($option_name, $option_data); +} + + + +/* +* use this filter to check if the role should allow registration email +* ../../../wordpress_docker/volumes/wp_volume/wp-includes/pluggable.php +* 2210 : $send_notification_to_user = apply_filters( 'wp_send_new_user_notification_to_user', true, $user ); +* +*/ +function send_registration_email($send, $user) { + $role_partner = Xtxpatch::ROLE_PARTNER; + $role_prof = Xtxpatch::ROLE_PROF; + $option_register_email = Xtxpatch::OPTION_REGISTER_EMAIL; + + $email_option = \Plgntls_xtx::get_option_safe($option_register_email['_name']); + + if (user_can($user, $role_prof)) { + $send = $email_option['is_email_prof']; + } + else if (user_can($user, $role_partner)) { + $send = $email_option['is_email_partner']; + } + + return $send; +} +add_filter( 'wp_send_new_user_notification_to_user', __NAMESPACE__.'\send_registration_email', 10, 2); + + + + + + + +/* +* use this filter to modify the message of the notification email +* you can use the specials custer expansions as $$$$ +* ../../../wordpress_docker/volumes/wp_volume/wp-content/plugins/easy-login-woocommerce/includes/class-xoo-el-form-handler.php +* 24 : add_filter( 'wp_new_user_notification_email', array( __CLASS__, 'newuser_notification_email' ), 20, 3 ); +* +*/ +function filter_regitration_email($wp_new_user_notification_email, $user, $blogname) { + $option_register_email = Xtxpatch::OPTION_REGISTER_EMAIL; + + $email_option = \Plgntls_xtx::get_option_safe($option_register_email['_name']); + $wp_new_user_notification_email['message'] = $email_option['email']; + return $wp_new_user_notification_email; +} +add_filter('wp_new_user_notification_email', __NAMESPACE__.'\filter_regitration_email', 21, 3); + + + + + +?> diff --git a/xtxpatch.php b/xtxpatch.php new file mode 100644 index 0000000..76c1371 --- /dev/null +++ b/xtxpatch.php @@ -0,0 +1,29 @@ +