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

modifier ce mode d'emplois :

+ +
+ + +
+ +
+ + +
+ + +
+ + + diff --git a/admin_menu.php b/admin_menu.php new file mode 100644 index 0000000..0a33803 --- /dev/null +++ b/admin_menu.php @@ -0,0 +1,45 @@ + diff --git a/change_id.php b/change_id.php new file mode 100644 index 0000000..a29462f --- /dev/null +++ b/change_id.php @@ -0,0 +1,147 @@ + there is no sens in forcing the logged_in, because it's already the default +* +* [custer_author_id] -> give current user the id of author +* [custer_author_id off] -> reset to logged_in +* +* ! anchors not workink for the moment +* //[custer_author_id set_anchor='anchor_name'] -> create anchor for author user +* //[custer_author_id anchor='anchor_name'] -> give current user the id stored in the anchor_name +* +*/ +function shortcode_author_id($options) { + $anchor_name = ''; + $option = ''; + $is_set_anchor = false; + + + /* + * set option value : + * 'off', 'author', 'set_anchor', or 'anchor' + * + */ + if (empty($options)) { + $option = 'author'; + } + if (isset($options['set_anchor'])) { + $option = 'author'; +// $is_set_anchor = true; +// $anchor_name = $options['set_anchor']; +// if (empty($anchor_name)) { +// return; +// } + unset($options['set_anchor']); + } + if (isset($options['anchor'])) { +// if ($is_set_anchor) { +// return; +// } +// $option = 'anchor'; +// $anchor_name = $options['anchor']; +// if (empty($anchor_name)) { +// return; +// } + unset($options['anchor']); + } + if (is_array($options) && in_array('off', $options)) { + $option = 'off'; + } + + if (empty($option)) { + return; + } + + + /* + * find id according to option + * if option is set_anchor, it sets it and return 0 + * + */ + $id = \CUSTER\find_id_wih_option($option, $anchor_name); + if ($id === 0) { + return; + } + + + /* + * set current user + * + */ + if ($is_set_anchor) { + \CUSTER\create_anchor($anchor_name, $id); + } + else { + wp_set_current_user($id); + } +} +add_shortcode('custer_author_id', __NAMESPACE__.'\shortcode_author_id'); + + + + + +function create_anchor($anchor_name, $id) { + $option_anchor = Custer::OPTION_ANCHOR; + + /* + * if needed, create the option + * + */ + if (false === get_option($option_anchor)) { + add_option($option_anchor, array()); + } + + + /* + * add or update the id for the anchor name + * + */ + $anchors = get_option($option_anchor); + $anchors[$anchor_name] = $id; + update_option($option_anchor, $anchors); +} + + + +/* +* find ids according to options : +* - author : change the current user to the author of the page or post +* - anchor : uses the id stored in options with anchor_name +* - off : reset current user to the id before the changes +* +*/ +function find_id_wih_option($option, $anchor_name) { + if ($option === 'author') { + $id = \CUSTER\get_author_id(); + Custer::set_current_user_backup(); + } + else if ($option === 'anchor') { + $id = \CUSTER\get_anchor_id($anchor_name); + Custer::set_current_user_backup(); + } + else if ($option === 'off') { + $id = Custer::reset_current_user_backup(); + } + return $id; +} + + + + +?> diff --git a/custer.php b/custer.php new file mode 100644 index 0000000..511745e --- /dev/null +++ b/custer.php @@ -0,0 +1,36 @@ + diff --git a/custer_class.php b/custer_class.php new file mode 100644 index 0000000..223b8ba --- /dev/null +++ b/custer_class.php @@ -0,0 +1,423 @@ +'toogle_admin_menu_url_custer', 'toggle'=>'toggle', 'show'=>'show', 'hide'=>'hide']; + const OPTION_TOGGLE_MENU = ['_name'=>'toggle_admin_menu_option_custer', 'show'=>'show', 'hide'=>'hide']; + const OPTION_NOTICE = [ + '_name'=>'notice_usage_custer', + '_default' => [ + 'style'=> +'h1 { + margin-bottom: 50px; +} +h2 { + margin-top: 50px; + font-size: 1.5rem; + border-bottom: 1px solid black; + padding-bottom: 10px; +} +section { + margin-left: 20px; +} +details { + padding: 10px; +} +details.border { + border: 1px solid black; +} +summary { + cursor: grab; +} +summary:has(h2) { + margin-bottom: 50px; +} +summary > * { + display: inline; +} +pre { + white-space: pre-wrap; +} +p, summary, pre, ul { + font-size: 1.3em; +} +ul { + list-style-type: disc; + list-style-position: outside; + margin-left: 15px; +} +div:has(>ul) { + border: 1px solid black; + padding: 10px; +} +mark { + background-color: lightgray; +} +div.example { + border-left: 1px solid black; + margin-left: 20px; + padding-left: 10px; +} +.flex { + display: flex; +} + .margin-right-10 { + margin-right: 10px; +} + .margin-top-20 { + margin-top: 20px; +}', + 'html'=> +'

mode d\'emplois de custer

+ +
+

1. emails : $$mot-clé$$

+ +

1.1. mots-clés custer

+ +
+

dans les emails, aussi bien dans le message, que dans le sujet ou la personne destinataire, on peut utiliser un mot-clé custer.

+

Qu\'est-ce que c\'est que ça ;) ?

+

+ C\'est simplement un mot-clé écrit entre doubles dollars : $$mot-clé$$
+ Lorsque un email contient ce schéma de mot entouré par des doubles dollars, il essaye de trouver la valeur associée à ce mot-clé.
+ Ces mots-clés correspondent en fait aux données que wordpress enregistre sur chaque personne. +

+
+ +

1.2. trouver les mots-clés

+ +
+

Ce n\'est pas toujours facile de savoir les mots-clés qui sont disponibles, sans aller voir le code directement, donc voici un exemple de liste de mots-clés pour une utilisatrice fictive :

+ +
+ exemples de mots-clés et leur valeurs (la liste exacte peut dépendre de la personne, mais cet exemple contient les valeurs les plus communes) +
+    admin_color : fresh
+    adresse_1 : 11 avenue Pauline
+    adresse_2 :
+    cgv : [\"\"]
+    code_postal : 92700
+    comment_shortcuts : false
+    compte-actif : Actif
+    description :
+    dismissed_wp_pointers :
+    display_name : AnneM
+    email_asso : prez@asso.fr
+    etablissement : École secondaire Waratinak
+    etat_carte : Renouvellement
+    etat_compte : carte valide
+    etat_paiement : aucun
+    etat_virement : [\"\"]
+    fin_de_validite : 19/03/2094
+    first_name : Anne
+    fonction : Prof
+    ID : 154
+    image_test_ratio :
+    justificatif : https://local-cipf-plugin.com/wp-content/uploads/de_fb_uploads/Attestation-1.pdf
+    last_name : Martin
+    livraison : Je préfère régler un supplément de 5 euros pour recevoir ma carte plastifiée
+    locale :
+    nickname : AnneM
+    niveau_denseignement : Secondaire
+    nom_asso : Mon asso
+    numero_de_la_carte : 20240403154
+    observations : J\'aime les bananes
+    paiement : Paypal
+    pays : Canada
+    photo_du_profil : https://local-cipf-plugin.com/wp-content/uploads/de_fb_uploads/Exemple_profil.jpg
+    president_asso : Le prez
+    rich_editing : true
+    session_tokens : {\"4003e23126f1697d7779ed03c9e45a70f2f6b34ce647a4507f573634d3c5469d\":{\"expiration\":1712437416,\"ip\":\"172.20.0.1\",\"ua\":\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0\",\"login\":1712264616},\"0b63f8dbef4446bcf94af3f007df0595c8bc39095eea4c9c5042cdb066c3aeef\":{\"expiration\":1712479930,\"ip\":\"172.20.0.1\",\"ua\":\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0\",\"login\":1712307130}}
+    show_admin_bar_front : true
+    somme_a_regler : 20
+    syntax_highlighting : true
+    tarif : Je suis hors réseau FIPF et je souhaite commander la carte à 15 €
+    telephone :
+    user_activation_key :
+    user_email : temlevorku@gufum.com
+    user_login : AnneM
+    user_login(1) :
+    user_nicename : annem
+    user_pass : $P$BwMtApJkSSYdE38By6PcLbs3Xk9crj.
+    user_registered : 14/03/2024
+    user_status :
+    user_url :
+    use_ssl :
+    ville : Colombes
+    wp_503463_capabilities : 1
+    wp_503463_user_level :
+    xoo-el-password :
+    xoo-el-rp-pass :
+    xoo-el-rp-pass-again :
+    xoo-el-sing-user :
+    xoo-el-username :
+    xoo_el_reg_email :
+    xoo_el_reg_fname :
+    xoo_el_reg_lname :
+    xoo_el_reg_pass :
+    xoo_el_reg_pass_again :
+    xoo_el_reg_terms :
+    xoo_el_reg_username :
+    __admin_email__ : hugulumu@protonmail.com
+    __author_page_url__ : https://local-cipf-plugin.com/author/annem/
+    __base_url__ : https://local-cipf-plugin.com
+    __user_post_url__ : 
+      
+
+ +

+ On peut remarquer que certains mot-clés sont sous la forme \'__mot_clé__\' avec deux doubles underscores avant et apres : "__"moit-clé"__"
+ Ce sont des mot-clés qui ne sont pas définis par worpdress de base, mais rajoutés par ce plugin (custer), plus d\'explication plus bas, dans la section suivante 1.3. +

+

Une méthode pour voir tous les mots-clés disponibles pour une personne connectée, c\'est d\'utiliser le shortcode [custer_user_info], comme expliqué dans la partie 2 de ce mode d\'emplois.

+
+ +

1.3. mots-clés speciaux \'__mot_clé__\'

+ +
+

Certains mot-clés sont entourés par des doubles underscore : __mot_clé__, ce sont des mot-clés definis par custer, voici leur signification :

+ +

+
+ +

1.4. exemple dans un email

+ +
+

Un exemple d\'utilisation dans un email, avec les données de l\'exemple précédent :

+
+
    +
  • destinataire : $$user_email$$
  • +
  • sujet : compte de $$nickname$$
  • +
  • message : Bonjour $$first_name$$ $$last_name$$, votre carte numero $$numero_de_la_carte$$ va bientot arriver a expiration, connectez-vous a votre compte pour la renouveler : $$__author_page_url__$$
  • +
+
+

et une fois les mot-clés convertis, cet email devient :

+
+
    +
  • destinataire : temlevorku@gufum.com
  • +
  • sujet : compte de AnneM
  • +
  • message : Bonjour Anne Martin, votre carte numero 20240403154 va bientot arriver a expiration, connectez-vous a votre compte pour la renouveler : https://local-cipf-plugin.com/author/annem/
  • +
+
+
+
+ + + +
+

2. shortcode : [custer_user_info mot-clé]

+ +

2.1. qu\'est-ce que c\'est ce shortcode

+ +
+

Les shortcodes sont des \'petits codes\' qu\'on peut ecrire sur les pages front de wordpress, et qui vont executer un code côté serveur, et souvent être remplacés par le resultat de ce code.

+

C\'est à dire, plus concretement, qu\'on peut écrire un shortcode [mon-shortcode], et qu\'il se transformera en autre chose, par exemple \'salut\'.

+

+Le shortcode de custer s\'appelle \'custer_user_info\', et il permet de retrouver la valeur du mot-clé qu\'on lui donne, les mêmes mots-clés que décrits dans la partie 1 de ce mode d\'emplois, à propos des emails.
+Cette fois-ci, au lieu d\'ecrire le mot-clé entre double dollars $$mot_clé$$, on l\'écrit dans le shortcode : [custer_user_info mot-clé]. +

+

+Le shortcode de custer possède plus d\'options que le simple mot-clé des emails, elles sont décrites dans les sections suivantes.
+Mais pourquoi utiliser un shortcode ET une version avec les doubles dollars ? Tout simplement parce que dans les emails les shortcodes ne marchent pas :) +

+
+ +

2.2. [custer_user_info]

+ +
+

En utilisant le shortcode seul, c\'est à dire uniquement avec son nom et sans aucun autre argument : [custer_user_info], on obtient une liste de tous les mots-clés et leur valeur de quelqu\'un. De qui ? He bien, soit de la personne connectée, soit de la personne qui à ecrit la page, mais ce point est expliqué dans la section 2.4.

+

C\'est cette méthode, le shortcode de base, qui a été utilisée pour obtenir la liste montrée plus haut dans la section email, et qui donne quelque chose comme :

+
+

[custer_user_info] -->

+
+display_name : AnneM
+etat_compte : carte valide
+fin_de_validite : 19/03/2094
+first_name : Anne
+fonction : Prof
+ID : 154
+nickname : AnneM
+... 
+			
+
+

Ce n\'est pas prévu pour être vraiment utilisé sur le site finale, uniquement comme un outil pour voir les valeurs disponibles.

+
+ +

2.3. [custer_user_info mot-clé]

+ +
+

C\'est sous cette forme de base que le shortcode va fonctionner : afficher la valeur d\'un mot-clé.

+

Par exemple, si la prof connectée s\'appelle AnneM sur le site, on peut obtenir ce surnom en ecrivant [custer_user_info nickname]

+

exemples :

+ +

C\'est assez simple, cependant il y à un point important qui est expliqué dans la section suivante, c\'est de choisir de qui on affiche les infos.

+
+ +

2.4. [custer_user_info mot-clé id=status]

+ +
+

+Il y a un petit piège dans la version de base du shortcode avec juste le mot-clé ([custer_user_info nickname]) :
+on ne veut pas obligatoirement que s\'affiche les infos de la personne connectée. +

+

Comment ca ?

+

+En effet, par exemple sur une page de profil, si elle peut être vue par d\'autre personnes que simplement soi-même, alors on ne veut pas voir ses propres informations quand on va sur le profil de quelqu\'un d\'autre. Pourtant, si le shortcode affiche le nom de la personne connectée, on à beau visiter la page de quelqu\'un d\'autre, c\'est bien nous la personne connectée.
+Généralement les pages de profils ne sont pas visibles publiquement, mais une personne avec les droits d\'administration peut y avoir accès, et rencontrer ce probleme. +

+

Le même probleme apparait, et de manière encore plus courante, sur un article : si on veut afficher le nom de la personne qui a écrit l\'article par exemple, on ne veut pas que ce soit le nom de la personne connectée qui s\'affiche, encore une fois !

+

Donc, pour palier à ce probleme, le shortcode possede 2 status : la personne connectée, ou la personne autrice de la page.

+

Et en fait, comme la plupart du temps ce qu\'on veut ce sont les infos de la personnes qui a crée la page, le shortcode est configuré par defaut pour afficher ces infos-la, et non pas les infos de la personne connectée. C\'est ça le piège ;)

+

Donc, pour choisir si on veut la valeur de la personne connectée ou de la personne à qui appartient la page, on peut rajouter un argument : \'id\' qui peut avoir deux valeurs :

+ +

Exemple, je suis connecté en tant que hugogogo et je vais voir la page de profil de AnneM :

+ +
+ +

2.5. [custer_user_info mot-clé if_empty=value]

+ +
+

Il y a un autre argument qu\'on peut rajouter à ce shortcode, c\'est la valeur qu\'on veut voir si le mot-clé ne trouve aucune valeur correspondante.

+

En effet, si on demande la description d\'une personne par exemple, et qu\'elle n\'est pas remplie, le shortcode va juste retourner rien du tout, par defaut.

+

Mais peut-etre qu\'on prefèrerait qu\'il affiche quelque chose comme \'description non-fournie\' ?

+

Dans ce cas c\'est très simple, il suffit de le préciser dans l\'arguement if_empty=\'description non-fournie\' (ça fonctionne avec id=\'\' aussi)

+

Exemples si Hugo visite la page d\'une personne qui n\'a pas de description, et la description de Hugo est "hugogogo est un rigolo" :

+ +
+
+ + + +
+

3. shortcode : [custer_author_id]

+ +

3.1. qu\'est-ce que c\'est ce nouveau shortcode la ??

+ +
+

Celui-ci est très simple, il permet de changer momentanément la valeur de la personne connectée par celle de la personne autrice de la page.

+

Pour bien comprendre, il faut d\'abord voir un exemple :

+

--> connecté en tant que hugogogo, visite la page de AnneM :

+
+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+

[custer_author_id]

+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+

[custer_author_id off]

+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+
+

ce code va donner le resultat suivant :

+
+

vous êtes connecté-e en tant que : hugogogo

+

vous êtes connecté-e en tant que : AnneM

+

vous êtes connecté-e en tant que : hugogogo

+
+

Qu\'est-ce qui s\'est passé ?

+

He bien en fait, lorsque le shortcode [custer_author_id] est ecrit, il modifie dans wordpress l\'information concernant la personne connectée, et il la rétablie lorsqu\'on ecrit le deuxième shortcode avec l\'argument \'off\' : [custer_author_id]

+

Mais attention ! Il modifie vraiment cette valeur pour tout wordpress, ce qui peut creer des problemes avec d\'autres éléments de la page, d\'autres plugins, d\'autres bout du thème, etc... Donc, il est recommandé de ne l\'utiliser que sur une courte portion de la page, pour un acte bien précis.

+
+ +

3.1. et pour finir, l\'argument \'important\'

+ +
+

Et ce n\'est pas tout :p Si par hasard, vous avez besoin d\'utiliser ce shortcode sur une portion de la page, et que dans cette portion vous avez quand-même besoin de récupérer une info sur la personne connectée avec le shortcode [custer_user_info mot-clé], la vraie personne connectée, comment faire ? Il y a une solution, en ajoutant l\'argument important au shortcode. Exemple :

+

--> connecté en tant que hugogogo, visite la page de AnneM :

+
+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+

[custer_author_id]

+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\' important]

+

[custer_author_id off]

+

vous êtes connecté-e en tant que : [custer_user_info nickname id=\'logged_in\']

+
+

ce code va donner le resultat suivant :

+
+

vous êtes connecté-e en tant que : hugogogo

+

vous êtes connecté-e en tant que : AnneM

+

vous êtes connecté-e en tant que : hugogogo

+

vous êtes connecté-e en tant que : hugogogo

+
+

On voit qu\'avec l\'argument important on retrouve la valeur de la vraie personne connectée

+

À noter que cet argument important n\'a pas d\'impact utilisé avec l\'autre id, ou en dehors du shortcode custer_author_id.

+ +
', + ], + ]; + + const USER_INFO_DATE_FORMAT = 'd/m/Y'; // for user_infos.php (date format : https://www.php.net/manual/fr/datetime.format.php) + const OPTION_ANCHOR = 'custer_anchor_ids'; + const QUERIES = [ + '__author_page_url__', + '__user_post_url__', + '__admin_email__', + '__base_url__', + ]; + + private static $_backup_current_user = null; + + + /* + * setter and getter for current user backup + * + */ + public static function set_current_user_backup() { + if (self::$_backup_current_user !== null) + return; + self::$_backup_current_user = get_current_user_id(); + } + public static function get_current_user_backup() { + return self::$_backup_current_user; + } + public static function reset_current_user_backup() { + $backup_user = self::$_backup_current_user; + if ($backup_user === null) { + $backup_user = get_current_user_id(); + } + self::$_backup_current_user = null; + return $backup_user; + } + + + +} + + + + +?> diff --git a/filter_mail.php b/filter_mail.php new file mode 100644 index 0000000..781baca --- /dev/null +++ b/filter_mail.php @@ -0,0 +1,154 @@ + $arg) { + if (is_array($arg)) { + $new_arg = array(); + foreach ($arg as $key => $value) { + $new_arg[$key] = preg_replace_callback($pattern, function($matches) use ($user_id) { + return \CUSTER\replace_words($matches, $user_id); + }, $value); + } + } + else { + $new_arg = preg_replace_callback($pattern, function($matches) use ($user_id) { + return \CUSTER\replace_words($matches, $user_id); + }, $arg); + } + $args[$arg_key] = $new_arg; + } + + return $args; +} +add_filter('wp_mail', __NAMESPACE__.'\filter_email_wp', 10, 1); + + +/* +* add this filter specifically for the new users, since otherwise we would not have their id +* +*/ +function filter_regitration_email_CIPF($wp_new_user_notification_email, $user, $blogname) { + $user_id = $user->ID; + $pattern = '/\$\$(.*?)\$\$/'; + + $old_body = $wp_new_user_notification_email['message']; + $new_body = preg_replace_callback($pattern, function($matches) use ($user_id) { + return \CUSTER\replace_words($matches, $user_id); + }, $old_body); + $wp_new_user_notification_email['message'] = $new_body; + + return $wp_new_user_notification_email; +} +add_filter('wp_new_user_notification_email', __NAMESPACE__.'\filter_regitration_email_CIPF', 22, 3); + + + + +function find_user_id_from_headers(&$args) { + if (empty($args)) { + return null; + } + if (!isset($args['headers'])) { + return null; + } + $headers = $args['headers']; + if (empty($headers)) { + return null; + } + if (!is_array($headers)) { + return null; + } + + $user_id = null; + + // isset returns false if the key exists but its value is null : 'user_id'=>null : https://stackoverflow.com/questions/3803282/check-if-value-isset-and-null + if (array_key_exists('user_id', $headers)) { + $user_id = $headers['user_id']; + unset($headers['user_id']); + $headers = array_values($headers); + } + + $args['headers'] = $headers; + return $user_id; +} + + + + + + +?> diff --git a/format_user_infos.php b/format_user_infos.php new file mode 100644 index 0000000..6b6e13c --- /dev/null +++ b/format_user_infos.php @@ -0,0 +1,145 @@ +$query; + } + + + + /* + * try to extract a single entity + * for example : ["string"] -> "string" + * or : ["", "string"] -> "string" + * + */ + $output = \CUSTER\extract_smallest_entity($output); + + + + /* + * if is not acf + * check if is a date + * and format it + * to create a DateTime from a time stamp : https://stackoverflow.com/a/12039058/9497573 + * + */ + if (!$is_acf) { + $timestamp = false; + if (is_string($output)) { + $timestamp = strtotime($output); + } + if ($timestamp !== false) { + //$date = new \DateTime('@' . $timestamp); + $date = date_create('@' . $timestamp); + $output = $date->format($output_date_format); + } + } + + + /* + * check options to format the result + * + */ + return \CUSTER\return_result($output, $if_empty); +} + + + + +?> diff --git a/get_user_id.php b/get_user_id.php new file mode 100644 index 0000000..029fe32 --- /dev/null +++ b/get_user_id.php @@ -0,0 +1,69 @@ + diff --git a/plgntls_class.php b/plgntls_class.php new file mode 100644 index 0000000..ad0f92e --- /dev/null +++ b/plgntls_class.php @@ -0,0 +1,1394 @@ + '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_custer { + /* + * 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 = 1; + private static $_DEBUG_INFOS = 2; + */ + 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 + * + */ + $option_admin_menu_name = self::$_option_toggle_menu['_name']; + self::$_option_toggle_menu['_name'] = $option_admin_menu_name . self::$_plugin_dir; + 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::$_plugin_dir; + self::$_options_list = 'list_of_options_for_this_plugin_'.self::$_plugin_dir; + 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 + // https://wordpress.stackexchange.com/questions/149802/get-plugins-doesnt-work-after-plugins-loaded + if (!function_exists('get_plugins')) { + include_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + $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']; + error_log("-debug: function '".$function."' (in ".$file.", line ".$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 * + * * * * * * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + '_name'=>'toggle_admin_menu_option_cipf', + */ + + private static $_option_toggle_menu = [ + '_name'=>'toggle_admin_menu_option_', + '_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, $name = false) { + /* + * 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; + } + } + + /* + * $name is true, + * add the _name entry to the output, + * it will overwrite any existing _name key + * if option is not an array, + * make it array and put the value into a '_value' key + * + * + */ + if ($name === true) { + if (!is_array($ret_option)) { + $value = $ret_option; + $ret_option = array(); + $ret_option['_value'] = $value; + } + $ret_option['_name'] = $option_name; + } + + 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", $class = '') { + $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 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 try to compare each fields and update them if needed + * - but is it not more heavy in computing actions ? + * - and what if the field is multi-nested array for example ? + * 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); + $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/queries.php b/queries.php new file mode 100644 index 0000000..93c55d0 --- /dev/null +++ b/queries.php @@ -0,0 +1,94 @@ + 'post', + 'author' => $user_id, + 'posts_per_page' => 1, + ); + $posts = get_posts($args); + + if (empty($posts)) { + $user_post_url = ''; + } + else { + $query = reset($posts); + $user_post_url = get_permalink($query->ID); + } + + return $user_post_url; +} + + + + +/* +* +* +*/ +function find_admin_email($user_id = null) { + $admin_email = get_option( 'admin_email' ); + return $admin_email; +} + + + + +/* +* +* +*/ +function find_base_url($user_id = null) { + $base_url = home_url(); + return $base_url; +} + + + + + + +?> diff --git a/user_infos.php b/user_infos.php new file mode 100644 index 0000000..fd0c81f --- /dev/null +++ b/user_infos.php @@ -0,0 +1,195 @@ + $value) { + $value = \CUSTER\extract_smallest_entity($value); + // if key does not already exist, simply add it and the value to new array + if (!isset($new_array[$key])) { + $new_array[$key] = $value; + continue; + } + // if key already exist, add a new key with a (number) suffix + $i = 1; + while (isset($new_array[$key.'('.$i.')'])) { + ++$i; + } + $new_array[$key.'('.$i.')'] = $value; + } + } + uksort($new_array, 'strnatcasecmp'); + return $new_array; +} + + +function output_list_front($array, $user_id, $if_empty) { + $output = '
    '; + foreach ($array as $key => $value) { + if (str_starts_with($key, '_')) { + if (!str_starts_with($key, '__')) { + continue ; + } + } + $value = \CUSTER\format_user_info($key, $user_id, $if_empty); + $output .= '
  • '; + $output .= ''; + $output .= $key; + $output .= ' : '; + $output .= $value; + $output .= ''; + $output .= '
  • '; + } + $output .= '
'; + return $output; +} + + + +/* +* shortcode to write user info of post author +* 0 or 1 argument, usage : +* - [custer_user_info] -> list of all availables infos +* - [custer_user_info user_email] -> display the email +* - [custer_user_info user_email user_login] -> display the email +* - [custer_user_info user_email id='logged_in'] -> display the email of the connected user +* - [custer_user_info user_email id='author'] -> display the email of the creator of the page/post +* - [custer_user_info user_email id='logged_in' important] -> display the email of the connected user, even if surrounded by shortcode 'custer_change_id' +* important keyword has no effect if id='author' +* - [custer_user_info user_email if_empty="value"] -> display 'value' if the user_email is empty or does not exist +* +*/ +function current_user_infos($atts) { + $is_important = false; + + + /* + * choose the default id target : + * - logged_in + * - author + * + $id_is = 'logged_in'; + */ + $id_is = 'author'; + $if_empty = ''; + + + /* + * has parameter 'id' ? + * has parameter important ? + * has parameter if_empty ? + * if yes, handle them and removes them from $atts + * + */ + if (is_array($atts)) { + if (isset($atts['id'])) { + $id_is = $atts['id']; + unset($atts['id']); + } + if ($id_is === 'logged_in') { + if (in_array('important', $atts)) { + $is_important = true; + $key = array_search('important', $atts); + unset($atts[$key]); + } + } + if (isset($atts['if_empty'])) { + $if_empty = $atts['if_empty']; + unset($atts['if_empty']); + } + } + + + /* + * should output all or a specific parameter ? + * + */ + $output_all = false; + if (empty($atts)) + $output_all = true; + else if (count($atts) === 0) + $output_all = true; + + + /* + * get id according to attributes, + * and the atts of the other shortcode 'custer_change_id' if in use + * + */ + if ($id_is === 'logged_in') { + if ($is_important) { + $user_id = Custer::get_current_user_backup(); + if ($user_id === null) + $user_id = get_current_user_id(); + } + else { + $user_id = get_current_user_id(); + } + } + else if ($id_is === 'author') { + $user_id = \CUSTER\get_author_id(); + } + else { + return ''; + } + + + + + /* + * output all the available parameters (for help) + * + */ + if ($output_all) { + $user_properties = (array) get_userdata($user_id)->data; + $user_metas = get_user_meta($user_id); + $queries = \CUSTER\get_queries($user_id, $if_empty); + $user_infos = \CUSTER\merge_arrays($user_metas, $user_properties, $queries); + return \CUSTER\output_list_front($user_infos, $user_id, $if_empty); + } + + + /* + * real purpose of this shortcode : + * only return the first argument + * + */ + if (is_array($atts)) + $query = $atts[0]; + else if (is_string($atts)) + $query = $atts; + else + return ''; + return \CUSTER\format_user_info($query, $user_id, $if_empty); +} +add_shortcode('custer_user_info', __NAMESPACE__.'\current_user_infos'); + + +?>