diff --git a/README.md b/README.md new file mode 100644 index 0000000..6437cd1 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# WPU Error Logs + +Make sense of your log files + + +## Roadmap + +- [ ] Admin widget +- [ ] Github actions +- [ ] Store errors in database +- [ ] View details for each error : help / error times, etc +- [ ] Push top errors via slack / mail +- [ ] Parse yesterday file if it exists. +- [ ] Ignore some logs diff --git a/inc/.htaccess b/inc/.htaccess new file mode 100644 index 0000000..b22492d --- /dev/null +++ b/inc/.htaccess @@ -0,0 +1,4 @@ +deny from all + + Allow From All + diff --git a/inc/WPUBaseAdminDatas/README.md b/inc/WPUBaseAdminDatas/README.md new file mode 100644 index 0000000..b82a15f --- /dev/null +++ b/inc/WPUBaseAdminDatas/README.md @@ -0,0 +1,47 @@ +WPU Base Admin Datas +--- + +Add admin datas and a database in your plugin. + +## Insert in the INIT hook + +```php +include dirname( __FILE__ ) . '/inc/WPUBaseAdminDatas/WPUBaseAdminDatas.php'; +$this->baseadmindatas = new \wpubaseplugin\WPUBaseAdminDatas(); +$this->baseadmindatas->init(array( + 'plugin_id' => 'my_plugin', + 'table_name' => 'my_table', + 'table_fields' => array( + 'value' => array( + 'public_name' => 'Value', + 'sql' => 'varchar(100) DEFAULT NULL' + ) + ) +)); +``` + +## Display table : + +- Default : + +```php +echo $this->baseadmindatas->get_admin_table(); +``` + +- Advanced : + +```php +$array_values = false; // ($array_values are automatically retrieved if not a valid array) +echo $this->baseadmindatas->get_admin_table( + $array_values, + array( + 'perpage' => 10, + 'columns' => array('creation' => 'Creation date') + ) +); +``` + +## TODO + +* Uninstall +* Default field type ( date => timestamp, text => varchar(100) ) diff --git a/inc/WPUBaseAdminDatas/WPUBaseAdminDatas.php b/inc/WPUBaseAdminDatas/WPUBaseAdminDatas.php new file mode 100644 index 0000000..f42f2cc --- /dev/null +++ b/inc/WPUBaseAdminDatas/WPUBaseAdminDatas.php @@ -0,0 +1,896 @@ +apply_settings($settings); + $this->check_database(); + $this->admin_export(); + add_action('admin_post_admindatas_' . $this->settings['plugin_id'], array(&$this, + 'delete_lines_postAction' + )); + if ($this->settings['can_edit']) { + add_action('admin_post_admindatas_edit_' . $this->settings['plugin_id'], array(&$this, + 'edit_line_postAction' + )); + } + if ($this->settings['can_create']) { + add_action('admin_post_admindatas_create_' . $this->settings['plugin_id'], array(&$this, + 'create_line_postAction' + )); + } + } + + public function apply_settings($settings) { + $default_settings = array( + 'plugin_id' => 'my_plugin', + 'admin_url' => 'admin.php', + 'table_name' => 'my_table', + 'table_fields' => array( + 'value_1' => array( + 'public_name' => 'Value 1' + ), + 'value_2' => array( + 'public_name' => 'Value 2' + ) + ) + ); + if (!is_array($settings)) { + $settings = $default_settings; + } + foreach ($default_settings as $key => $val) { + if (!isset($settings[$key])) { + $settings[$key] = $val; + } + } + + // Remove id column + if (isset($settings['table_fields']['id'])) { + unset($settings['table_fields']['id']); + } + // Remove creation column + if (isset($settings['table_fields']['creation'])) { + unset($settings['table_fields']['creation']); + } + + // Build query + foreach ($settings['table_fields'] as $id => $field) { + if (!isset($field['public_name'])) { + $settings['table_fields'][$id]['public_name'] = $id; + } + if (!isset($field['type'])) { + $settings['table_fields'][$id]['type'] = isset($field['type']) ? $field['type'] : 'varchar'; + } + if (!isset($field['field_type']) || !in_array($field['field_type'], $this->field_types)) { + $settings['table_fields'][$id]['field_type'] = 'text'; + } + if (!isset($field['edit'])) { + $settings['table_fields'][$id]['edit'] = false; + } + if (!isset($field['create'])) { + $settings['table_fields'][$id]['create'] = false; + } + } + + if (!isset($settings['user_level'])) { + $settings['user_level'] = $this->user_level; + } + + if (!isset($settings['handle_database'])) { + $settings['handle_database'] = true; + } + + if (!isset($settings['can_create']) || !current_user_can($settings['user_level'])) { + $settings['can_create'] = false; + } + + if (!isset($settings['can_edit']) || !current_user_can($settings['user_level'])) { + $settings['can_edit'] = $settings['can_create']; + } + + $this->settings = $settings; + + $this->user_level = $settings['user_level']; + $this->pageid = $this->settings['plugin_id']; + if (isset($this->settings['plugin_pageid'])) { + $this->pageid = $this->settings['plugin_pageid']; + } + $this->pagename = admin_url($this->settings['admin_url'] . '?page=' . $this->pageid); + $this->sql_option_name = $this->settings['plugin_id'] . '_' . $this->settings['table_name'] . '_version'; + + global $wpdb; + $this->tablename = $wpdb->prefix . $this->settings['table_name']; + } + + /* ---------------------------------------------------------- + Database Creation + ---------------------------------------------------------- */ + + public function drop_database() { + global $wpdb; + $wpdb->query("DROP TABLE IF EXISTS " . $this->tablename); + delete_option($this->sql_option_name); + } + + public function check_database() { + if ($this->settings['handle_database']) { + return; + } + global $wpdb; + + // Assemble fields + $fields_query = array( + 'id mediumint(8) unsigned NOT NULL auto_increment', + 'creation TIMESTAMP DEFAULT CURRENT_TIMESTAMP', + 'PRIMARY KEY (id)' + ); + + // Build query + /* Two spaces to avoid temporary table WP fix */ + $sql_query = "CREATE TABLE " . $this->tablename; + $sql_query .= " (\n" . implode(",\n", $fields_query) . "\n)"; + $sql_query .= " DEFAULT CHARSET=utf8;"; + + // If query has changed since last time + $sql_md5 = md5(serialize($this->settings['table_fields'])); + $sql_option_value = get_option($this->sql_option_name); + if ($sql_md5 != $sql_option_value) { + // Update or create table + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + + // Create table + maybe_create_table($this->tablename, $sql_query); + + foreach ($this->settings['table_fields'] as $column_name => $col) { + switch ($col['type']) { + case 'varchar': + $col_sql = 'varchar(100) DEFAULT NULL'; + break; + case 'number': + $col_sql = 'MEDIUMINT UNSIGNED'; + break; + case 'timestamp': + $col_sql = 'TIMESTAMP'; + break; + default: + $col_sql = $col['sql']; + } + + maybe_add_column($this->tablename, $column_name, 'ALTER TABLE ' . $this->tablename . ' ADD ' . $column_name . ' ' . $col_sql); + } + + // Update option hash + update_option($this->sql_option_name, $sql_md5); + } + } + + /* ---------------------------------------------------------- + Utilities : Requests + ---------------------------------------------------------- */ + + public function get_pager_limit($perpage = false, $req_details = '') { + global $wpdb; + + // Ensure good format for table name + if (empty($this->tablename) || !preg_match('/^([A-Za-z0-9_-]+)$/', $this->tablename)) { + return array( + 'pagenum' => 0, + 'max_pages' => 0, + 'limit' => '' + ); + } + + // Ensure good format for perpage + if (empty($perpage) || !is_numeric($perpage)) { + $perpage = $this->default_perpage; + } + + // Get number of elements in table + $elements_count = $wpdb->get_var("SELECT COUNT(*) FROM " . $this->tablename . $req_details); + + // Get max page number + $max_pages = ceil($elements_count / $perpage); + + // Obtain Page Number + $pagenum = (isset($_GET['pagenum']) && is_numeric($_GET['pagenum']) ? $_GET['pagenum'] : 1); + $pagenum = min($pagenum, $max_pages); + + // Set SQL limit + $limit_min = max(0, ($pagenum * $perpage - $perpage)); + $limit = 'LIMIT ' . $limit_min . ', ' . $perpage; + + return array( + 'pagenum' => $pagenum, + 'max_pages' => $max_pages, + 'max_elements' => $elements_count, + 'limit' => $limit + ); + } + + /* ---------------------------------------------------------- + Lines + ---------------------------------------------------------- */ + + public function get_line($line_id) { + return $this->get_line_by('id', $line_id, '%d'); + } + + public function get_line_by($field, $value = '', $value_type = '%s') { + global $wpdb; + return $wpdb->get_row($wpdb->prepare("SELECT * FROM " . $this->tablename . " WHERE " . $field . "=" . $value_type, $value), ARRAY_A); + } + + /* ---------------------------------------------------------- + Create line + ---------------------------------------------------------- */ + + public function create_line_postAction() { + $_return_value = false; + if (current_user_can($this->user_level) && !empty($_POST) && isset($_POST['admindatas_fields'], $_POST['page']) && is_array($_POST['admindatas_fields'])) { + $action_id = 'action-create-form-admin-datas-' . $_POST['page']; + if (isset($_POST[$action_id]) && wp_verify_nonce($_POST[$action_id], 'action-create-form-' . $_POST['page'])) { + $_return_value = $this->create_line($_POST['admindatas_fields']); + } + } + if (isset($_POST['page']) && is_numeric($_return_value)) { + $_back_url = $this->pagename . '&item_id=' . $_return_value; + if (isset($_POST['backquery'])) { + $_back_url = add_query_arg(array('backquery' => $_POST['backquery']), $_back_url); + } + wp_redirect($_back_url); + die; + } + } + + public function create_line($datas) { + $_datas_create = array(); + $_return_value = false; + foreach ($this->settings['table_fields'] as $id => $field) { + if (!isset($datas[$id])) { + continue; + } + /* Invalid field */ + if (!$this->validate_field_value($field, $datas[$id])) { + continue; + } + $_datas_create[$id] = $datas[$id]; + } + if (!empty($_datas_create)) { + global $wpdb; + $_return_value = $wpdb->insert( + $this->tablename, + $_datas_create + ); + if ($wpdb->last_error !== '') { + $wpdb->print_error(); + } + } + if ($_return_value) { + $_return_value = $wpdb->insert_id; + } + return $_return_value; + } + + /* ---------------------------------------------------------- + Edit line + ---------------------------------------------------------- */ + + public function edit_line_postAction() { + if (current_user_can($this->user_level) && !empty($_POST) && isset($_POST['edit_line'], $_POST['admindatas_fields'], $_POST['page']) && is_numeric($_POST['edit_line']) && is_array($_POST['admindatas_fields'])) { + $action_id = 'action-edit-form-admin-datas-' . $_POST['page']; + if (isset($_POST[$action_id]) && wp_verify_nonce($_POST[$action_id], 'action-edit-form-' . $_POST['page'])) { + $this->edit_line($_POST['edit_line'], $_POST['admindatas_fields']); + } + } + if (isset($_POST['page'], $_POST['edit_line']) && is_numeric($_POST['edit_line'])) { + $_back_url = $this->pagename . '&item_id=' . $_POST['edit_line']; + if (isset($_POST['backquery'])) { + $_back_url = add_query_arg(array('backquery' => $_POST['backquery']), $_back_url); + } + wp_redirect($_back_url); + die; + } + } + + public function edit_line($item_id, $new_datas) { + $_datas = $this->get_line($item_id); + $_datas_update = array(); + foreach ($new_datas as $key => $var) { + /* Same value */ + if ($_datas[$key] == $var) { + continue; + } + /* Fake field */ + if (!isset($this->settings['table_fields'][$key])) { + continue; + } + /* Invalid field */ + if (!$this->validate_field_value($this->settings['table_fields'][$key], $var)) { + continue; + } + $_datas_update[$key] = $var; + } + + if (!empty($_datas_update)) { + global $wpdb; + $wpdb->update( + $this->tablename, + $_datas_update, + array('ID' => $item_id) + ); + } + } + + /* ---------------------------------------------------------- + Validate + ---------------------------------------------------------- */ + + public function validate_field_value($field, $value) { + switch ($field['field_type']) { + case 'email': + return !!filter_var($value, FILTER_VALIDATE_EMAIL); + break; + + case 'url': + return !!filter_var($value, FILTER_VALIDATE_URL); + break; + + case 'number': + return is_numeric($value); + break; + + default: + return true; + } + + } + + /* ---------------------------------------------------------- + Delete lines + ---------------------------------------------------------- */ + + public function delete_lines_postAction() { + if (current_user_can($this->user_level) && !empty($_POST) && isset($_POST['select_line'], $_POST['page']) && is_array($_POST['select_line'])) { + $action_id = 'action-main-form-admin-datas-' . $_POST['page']; + if (isset($_POST[$action_id]) && wp_verify_nonce($_POST[$action_id], 'action-main-form-' . $_POST['page'])) { + $this->delete_lines($_POST['select_line']); + } + } + if (isset($_POST['page'])) { + wp_redirect($this->pagename); + die; + } + } + + public function delete_lines($lines = array()) { + $_lines = array(); + foreach ($lines as $line) { + // Stop if a line is not valid + if (!is_numeric($line)) { + break; + } + $_lines[] = $line; + } + if (!empty($_lines)) { + global $wpdb; + $wpdb->query( + "DELETE FROM " . $this->tablename . " WHERE ID IN(" . implode(",", $_lines) . ");" + ); + } + } + + /* ---------------------------------------------------------- + Utilities : Export + ---------------------------------------------------------- */ + + public function export_array_to_csv($array, $name) { + if (isset($array[0])) { + header('Content-Type: application/csv'); + header('Content-Disposition: attachment; filename=export-list-' . $name . '-' . date_i18n('y-m-d') . '.csv'); + header('Pragma: no-cache'); + echo implode(';', array_keys($array[0])) . "\n"; + foreach ($array as $line) { + echo implode(';', $line) . "\n"; + } + die; + } + } + + /* ---------------------------------------------------------- + Export all + ---------------------------------------------------------- */ + + public function admin_export() { + if (!is_admin()) { + return; + } + if (!current_user_can($this->settings['user_level'])) { + return; + } + if (!$this->tablename) { + return; + } + if (!isset($_GET['wpubaseadmindatas_export']) || ($_GET['wpubaseadmindatas_export'] != $this->tablename)) { + return; + } + $this->export_datas(); + } + + /* Thanks to https://stackoverflow.com/a/55482704 */ + public function export_datas() { + global $wpdb; + + $query = "SELECT * FROM " . $this->tablename . " WHERE 1=1"; + $columns = $wpdb->get_results("SHOW COLUMNS FROM " . $this->tablename, ARRAY_A); + + /* Filters */ + if (isset($_GET['filter_key'], $_GET['filter_value'])) { + $has_filter_key = true; + $query .= " AND " . esc_sql($_GET['filter_key']) . " = '" . esc_sql($_GET['filter_value']) . "'"; + } + + /* Search */ + if (isset($_GET['where_text']) && $_GET['where_text']) { + $where = array(); + $where_text = trim($_GET['where_text']); + foreach ($columns as $field) { + $id = $field['Field']; + if ($id != 'id' && $id != 'creation') { + $where[] = "$id LIKE '%" . esc_sql($_GET['where_text']) . "%'"; + } + } + $query .= " AND (" . implode(' OR ', $where) . ")"; + } + + $rows = $wpdb->get_results($query, ARRAY_A); + + $fp = fopen('php://output', 'w'); + + header('Content-Type: application/csv'); + header("Content-Disposition: attachment; filename=export-" . $this->tablename . "-" . date_i18n('Ymd-His') . ".csv"); + header('Pragma: no-cache'); + + if (is_array($columns) && !empty($columns)) { + fputcsv($fp, array_column($columns, 'Field')); + } + + if (!empty($rows)) { + foreach ($rows as $row) { + fputcsv($fp, $row); + } + } + + fclose($fp); + exit; + } + + /* ---------------------------------------------------------- + Utilities : Display + ---------------------------------------------------------- */ + + public function get_admin_view($values = array(), $args = array()) { + if (isset($_GET['item_id']) && is_numeric($_GET['item_id'])) { + $_content = $this->get_admin_item($_GET['item_id']); + } elseif (isset($_GET['create'])) { + $_content = $this->get_admin_item(); + } else { + $args['is_admin_view'] = true; + $_content = $this->get_admin_table($values, $args); + } + + return $_content; + } + + public function get_admin_item($item_id = false) { + $_html = ''; + + $_back_query = ''; + $_back_url_args = array(); + $page_id = $this->get_page_id(); + $datas = $item_id ? $this->get_line($item_id) : array(); + + /* Save back query if valid */ + if (isset($_GET['backquery'])) { + $_back_args = json_decode(base64_decode($_GET['backquery'])); + if (is_object($_back_args)) { + $_back_url_args = (array) $_back_args; + $_back_query = $_GET['backquery']; + } + } + + /* Diff between edit & create form */ + $_has_form = ($this->settings['can_edit'] && $item_id) || ($this->settings['can_create'] && !$item_id); + + /* Actions */ + $_form_action = $item_id ? 'admindatas_edit_' : 'admindatas_create_'; + $_form_nonce = $item_id ? 'action-edit-form-' : 'action-create-form-'; + + if ($_has_form) { + $_html .= '
'; + $_html .= ''; + $_html .= ''; + if ($_back_query) { + $_html .= ''; + } + if ($item_id) { + $_html .= ''; + } + $_html .= wp_nonce_field($_form_nonce . $page_id, $_form_nonce . 'admin-datas-' . $page_id, true, false); + } + + if ($item_id) { + $_html .= '

#' . $item_id . '

'; + } else { + $_html .= '

' . __('New Post') . '

'; + } + $_html .= '' . __('Back') . ''; + + $_html .= ''; + foreach ($this->settings['table_fields'] as $id => $field) { + $value = isset($datas[$id]) ? htmlspecialchars($datas[$id], ENT_QUOTES, "UTF-8") : ''; + + $_fieldId = 'admindatas_fields_' . $id; + $_html .= ''; + $_html .= ''; + $_html .= ''; + $_html .= ''; + } + $_html .= '
'; + if ($_has_form) { + if (!isset($field['field_attributes'])) { + $field['field_attributes'] = ''; + } + switch ($field['field_type']) { + case 'textarea': + $_html .= ''; + break; + case 'true_false': + $_html .= ''; + $_html .= ''; + break; + default: + $_html .= ''; + } + } else { + $_html .= '' . esc_html($value) . ''; + } + $_html .= '
'; + + if ($_has_form) { + $_html .= get_submit_button(__('Submit'), '', 'submit', false); + $_html .= '
'; + } + + return $_html; + } + + public function get_admin_table($values = array(), $args = array()) { + global $wpdb; + + $pagination = ''; + + $is_admin_view = isset($args['is_admin_view']) && $args['is_admin_view']; + $export_url = 'index.php?wpubaseadmindatas_export=' . $this->tablename; + $export_url_base = $export_url; + + if (!is_array($args)) { + $args = array(); + } + + // Per page + if (!isset($args['perpage']) || !is_numeric($args['perpage'])) { + $args['perpage'] = $this->default_perpage; + } + + // Add ID + $default_columns = array( + 'creation' => array( + 'has_filter' => false, + 'label' => 'Creation date' + ), + 'id' => array( + 'has_filter' => false, + 'label' => 'ID' + ) + ); + $base_columns = array(); + $args['primary_column'] = 'id'; + if (isset($args['columns']) && is_array($args['columns'])) { + $new_args_column = array(); + foreach ($args['columns'] as $id => $field) { + if (!isset($args['primary_column'])) { + $args['primary_column'] = $id; + } + $has_filter = false; + $field_label = $field; + if (is_array($field)) { + $field_label = $id; + if (isset($field['label'])) { + $field_label = $field['label']; + } + if (isset($field['has_filter'])) { + $has_filter = $field['has_filter']; + } + } + $new_args_column[$id] = array( + 'has_filter' => $has_filter, + 'label' => $field_label + ); + } + $args['columns'] = $new_args_column; + } + $base_columns = array_merge($base_columns, $default_columns); + + // Default columns + if (!isset($args['columns'])) { + $args['columns'] = $base_columns; + } + + // Filter results + $where_glue = (isset($_GET['where_glue']) && in_array($_GET['where_glue'], array('AND', 'OR'))) ? $_GET['where_glue'] : 'AND'; + $where = array(); + $where_text = isset($_GET['where_text']) ? trim($_GET['where_text']) : ''; + if (!empty($where_text)) { + $where_glue = 'OR'; + $export_url .= '&where_text=' . esc_html($_GET['where_text']); + foreach ($args['columns'] as $id => $name) { + if ($id != 'id' && $id != 'creation') { + $where[] = "$id LIKE '%" . esc_sql($where_text) . "%'"; + } + } + } + + // Filter + $has_filter_key = false; + if (isset($_GET['filter_key'], $_GET['filter_value'])) { + $has_filter_key = true; + $export_url .= '&filter_key=' . esc_html($_GET['filter_key']) . '&filter_value=' . esc_html($_GET['filter_value']); + $where[] = esc_sql($_GET['filter_key']) . " = '" . esc_sql($_GET['filter_value']) . "'"; + } + + // Order results + if (!isset($args['order'])) { + $args['order'] = isset($_GET['order']) && in_array($_GET['order'], array('asc', 'desc')) ? $_GET['order'] : 'desc'; + } + + if (!isset($args['orderby'])) { + $args['orderby'] = isset($_GET['orderby']) && array_key_exists($_GET['orderby'], $args['columns']) ? $_GET['orderby'] : 'id'; + } + + // Build filter query + $sql_where = ''; + if (!empty($where)) { + $sql_where .= " WHERE " . implode(" " . $where_glue . " ", $where) . " "; + } + + // Build order + $sql_order = ' ORDER BY ' . $args['orderby'] . ' ' . strtoupper($args['order']) . ' '; + + // Default pagenum & max pages + if (!isset($args['pagenum']) || !isset($args['max_pages']) || !isset($args['limit']) || !isset($args['max_elements'])) { + $pager = $this->get_pager_limit($args['perpage'], $sql_where); + if (!isset($args['pagenum'])) { + $args['pagenum'] = $pager['pagenum']; + } + if (!isset($args['max_pages'])) { + $args['max_pages'] = $pager['max_pages']; + } + if (!isset($args['limit'])) { + $args['limit'] = $pager['limit']; + } + if (!isset($args['max_elements'])) { + $args['max_elements'] = $pager['max_elements']; + } + } + + // Default list + $total_nb = 0; + if (empty($values) || !is_array($values)) { + $columns = array_keys($args['columns']); + $query = "SELECT " . implode(", ", $columns) . " FROM " . $this->tablename . " " . $sql_where . " " . $sql_order . " " . $args['limit']; + $query_total = "SELECT count(" . $columns[0] . ") FROM " . $this->tablename . " " . $sql_where; + $values = $wpdb->get_results($query); + $total_nb = $wpdb->get_var($query_total); + } + + $page_id = $this->get_page_id(); + if (isset($args['page_id'])) { + $page_id = $args['page_id']; + } + + $url_items_clear = array( + 'order' => $args['order'], + 'orderby' => $args['orderby'], + 'page' => $page_id + ); + $url_items = $url_items_clear; + $url_items['pagenum'] = '%#%'; + $url_items['where_glue'] = $where_glue; + $url_items['where_text'] = $where_text; + + /* Back query used in single page */ + $url_items_edit = $url_items; + unset($url_items_edit['pagenum']); + $_back_query = base64_encode(json_encode($url_items_edit)); + + $page_links = paginate_links(array( + 'base' => add_query_arg($url_items), + 'format' => '', + 'prev_text' => '«', + 'next_text' => '»', + 'total' => $args['max_pages'], + 'current' => $args['pagenum'] + )); + + $start_element = max(0, ($args['pagenum'] - 1) * $args['perpage'] + 1); + $end_element = min($args['pagenum'] * $args['perpage'], $args['max_elements']); + $pagination = '
'; + $pagination .= '
'; + $pagination .= sprintf(__('Items %s - %s', $this->settings['plugin_id']), $start_element, $end_element); + if ($total_nb) { + $pagination .= ' / ' . $total_nb; + } + $pagination .= '
'; + if ($page_links) { + $pagination .= '
' . $page_links . '
'; + } + $pagination .= '
'; + + $clear_form = ''; + if ($has_filter_key) { + $clear_form .= '

'; + $clear_form .= sprintf(__('Filter : %s • Value : %s', $this->settings['plugin_id']), esc_html($_GET['filter_key']), esc_html($_GET['filter_value'])); + $clear_form .= '
' . __('Reset') . ''; + $clear_form .= '

'; + } + + $search_form = '

'; + + $has_id = isset($values[0]) && is_object($values[0]) && isset($values[0]->id); + + $content = '
'; + $content .= ''; + $content .= ''; + $content .= wp_nonce_field('action-main-form-' . $page_id, 'action-main-form-admin-datas-' . $page_id, true, false); + if ($has_id && $is_admin_view && $this->settings['can_create']) { + $new_url = add_query_arg(array('backquery' => $_back_query), $this->pagename . '&create=1'); + $content .= '

' . __('New Post') . '

'; + } + $content .= ''; + if (isset($args['columns']) && is_array($args['columns']) && !empty($args['columns'])) { + $labels = ''; + if ($has_id) { + $labels .= ''; + } + foreach ($args['columns'] as $id_col => $col_info) { + $url_items_tmp = $url_items; + $url_items_tmp['pagenum'] = 1; + $url_items_tmp['orderby'] = $id_col; + $url_items_tmp['order'] = $args['order'] == 'asc' ? 'desc' : 'asc'; + $sort_link = add_query_arg($url_items_tmp); + $labels .= ''; + } + if ($has_id && $is_admin_view) { + $labels .= ''; + } + $labels .= ''; + $content .= '' . sprintf($labels, 1) . ''; + $content .= '' . sprintf($labels, 2) . ''; + } + $content .= ''; + foreach ($values as $id => $vals) { + $content .= ''; + if ($has_id) { + $content .= ''; + } + foreach ($vals as $cell_id => $val) { + $val = (empty($val) ? ' ' : $val); + $content .= ''; + } + if ($has_id && $is_admin_view) { + $edit_url = add_query_arg(array('backquery' => $_back_query), $this->pagename . '&item_id=' . $vals->id); + $content .= ''; + } + $content .= ''; + } + $content .= ''; + $content .= '
' . $col_info['label'] . '
'; + $cell_content = apply_filters('wpubaseadmindatas_cellcontent', $val, $cell_id, $this->settings); + + // Allow filter + if (isset($args['columns'][$cell_id]['has_filter']) && $args['columns'][$cell_id]['has_filter']) { + $filtered_url = add_query_arg(array( + 'filter_key' => $cell_id, + 'filter_value' => $cell_content + ), $this->pagename); + $cell_content = '' . $cell_content . ''; + } + + $content .= $cell_content; + if ($cell_id == $args['primary_column']) { + $content .= ''; + } + $content .= '' . ($this->settings['can_edit'] ? __('Edit') : __('View')) . '
'; + if ($has_id) { + $content .= '

' . get_submit_button(__('Delete'), 'delete', 'delete_lines', false) . '

'; + } + $content .= '
'; + $content .= $clear_form; + $content .= $search_form; + $content .= $pagination; + $content .= '' . __('Export all') . ''; + if ($where_text || $has_filter_key) { + $content .= ' ' . __('Export filtered view') . ''; + } + $content .= << +.admindatas-search-filter{ + text-align: right; +} +.admindatas-search-form {margin:1em 0;} +@media (min-width:768px) { + .admindatas-delete-button {float: left;} +} +@media (max-width:767px) { + .admindatas-search-form .search-box {position: relative!important;height:auto;margin:0;} +} + +HTML; + return $content; + } + + /* ---------------------------------------------------------- + Helpers + ---------------------------------------------------------- */ + + public function get_page_id() { + $screen = get_current_screen(); + $page_id = ''; + if (property_exists($screen, 'parent_base')) { + $page_id = $screen->parent_base; + } + return $page_id; + } +} diff --git a/inc/WPUBaseAdminPage/README.md b/inc/WPUBaseAdminPage/README.md new file mode 100644 index 0000000..f357864 --- /dev/null +++ b/inc/WPUBaseAdminPage/README.md @@ -0,0 +1,45 @@ +WPU Base Admin Page +--- + +Add an admin page in your plugin. + +## Insert in the plugins_loaded hook + +```php +$admin_pages = array( + 'main' => array( + 'menu_name' => 'Base plugin', + 'name' => 'Main page', + 'settings_link' => true, + 'settings_name' => 'Settings', + 'function_content' => array(&$this, + 'page_content__main' + ), + 'function_action' => array(&$this, + 'page_action__main' + ) + ), + 'subpage' => array( + 'parent' => 'main', + 'name' => 'Subpage page', + 'function_content' => array(&$this, + 'page_content__subpage' + ), + 'function_action' => array(&$this, + 'page_action__subpage' + ) + ) +); + +$pages_options = array( + 'id' => 'wpubaseplugin', + 'level' => 'manage_options', + 'basename' => plugin_basename(__FILE__) +); + +// Init admin page +include dirname( __FILE__ ) . '/inc/WPUBaseAdminPage/WPUBaseAdminPage.php'; +$this->adminpages = new \wpubaseplugin\WPUBaseAdminPage(); +$this->adminpages->init($pages_options, $admin_pages); + +``` diff --git a/inc/WPUBaseAdminPage/WPUBaseAdminPage.php b/inc/WPUBaseAdminPage/WPUBaseAdminPage.php new file mode 100644 index 0000000..16cd3e0 --- /dev/null +++ b/inc/WPUBaseAdminPage/WPUBaseAdminPage.php @@ -0,0 +1,274 @@ +options = $options; + $this->pages = $pages; + $this->prefix = $this->options['id'] . '-'; + $this->pages = $this->set_pages($this->pages); + add_action('admin_menu', array(&$this, + 'set_admin_menu' + )); + add_action('admin_bar_menu', array(&$this, + 'set_adminbar_menu' + ), 100); + add_filter("plugin_action_links_" . $this->options['basename'], array(&$this, + 'add_settings_link' + )); + // Only on a plugin admin page + $current_page = $this->get_page(); + if (array_key_exists($current_page, $this->pages)) { + add_action('admin_post_' . $this->options['id'], array(&$this, + 'set_admin_page_main_postAction' + )); + } + foreach ($this->pages as $p_id => $p) { + if ($p_id == $current_page) { + foreach ($p['actions'] as $action) { + add_action($action[0], $action[1]); + } + foreach ($p['filters'] as $filter) { + add_filter($filter[0], $filter[1]); + } + } + } + } + + public function set_pages($pages) { + foreach ($pages as $id => $page) { + $page['id'] = $this->prefix . $id; + $page['url'] = admin_url('admin.php?page=' . $page['id']); + if (!isset($page['section'])) { + $page['section'] = ''; + } else { + $path = (strpos($page['section'], '?') !== false ? '&' : '?') . 'page=' . $page['id']; + $page['url'] = admin_url($page['section'] . $path); + } + if (!isset($page['name'])) { + $page['name'] = $id; + } + if (!isset($page['menu_name'])) { + $page['menu_name'] = $page['name']; + } + if (!isset($page['settings_name'])) { + $page['settings_name'] = $page['name']; + } + if (!isset($page['parent'])) { + $page['parent'] = ''; + } + if (!isset($page['actions'])) { + $page['actions'] = array(); + } + if (!isset($page['filters'])) { + $page['filters'] = array(); + } + if (!isset($page['display_banner_menu'])) { + $page['display_banner_menu'] = false; + } + if (!isset($page['function_content'])) { + $page['function_content'] = array(&$this, + 'page_content__' . $id + ); + } + if (!isset($page['function_action'])) { + $page['function_action'] = array(&$this, + 'page_action__' . $id + ); + } + if (!isset($page['level'])) { + $page['level'] = $this->options['level']; + } + if (!isset($page['icon_url'])) { + $page['icon_url'] = ''; + } + if (!isset($page['has_file'])) { + $page['has_file'] = false; + } + if (!isset($page['settings_link'])) { + $page['settings_link'] = false; + } + if (!isset($page['has_form'])) { + $page['has_form'] = true; + } + if (!isset($page['page_help'])) { + $page['page_help'] = false; + } + $pages[$id] = $page; + } + return $pages; + } + + public function set_admin_menu() { + $parent = false; + foreach ($this->pages as $id => $page) { + + $page_id = $page['id']; + $page_action = array(&$this, + 'set_admin_page_main' + ); + $page_title = $page['name']; + if (isset($page['page_title'])) { + $page_title = $page['page_title']; + } + + // A parent is defined + if (array_key_exists($page['parent'], $this->pages)) { + $this->page_hook = add_submenu_page($this->prefix . $page['parent'], $page_title, $page['menu_name'], $page['level'], $page_id, $page_action); + // A section is defined + } elseif (!empty($page['section'])) { + $this->page_hook = add_submenu_page($page['section'], $page_title, $page['menu_name'], $page['level'], $page_id, $page_action); + } else { + // Create a parent menu page + add_menu_page($page['name'], $page['menu_name'], $page['level'], $page_id, $page_action, $page['icon_url']); + $this->page_hook = add_submenu_page($page_id, $page_title, $page['name'], $page['level'], $page_id, $page_action); + } + add_action('load-' . $this->page_hook, array(&$this, 'add_help'), 10, 1); + } + } + + public function add_help($arg) { + + $screen = get_current_screen(); + if (!isset($_GET['page']) || !is_object($screen) || !property_exists($screen, 'base')) { + return; + } + $base_str = str_replace(array($this->prefix, 'toplevel_page_'), '', $screen->base); + // Add help tabs + if (isset($this->pages[$base_str]['page_help']) && !empty($this->pages[$base_str]['page_help'])) { + $help_page = $this->pages[$base_str]['page_help']; + // If multiple one pages are available + if (is_array($help_page)) { + foreach ($help_page as $id => $help_tab) { + $help_tab['title'] = isset($help_tab['title']) ? $help_tab['title'] : $id; + $help_tab['content'] = isset($help_tab['content']) ? $help_tab['content'] : $help_tab['title']; + $screen->add_help_tab(array( + 'id' => 'help-' . $base_str . '-' . $id, + 'title' => $help_tab['title'], + 'content' => $help_tab['content'] + )); + } + } else { + // If only one tab available + $screen->add_help_tab(array( + 'id' => 'help-' . $base_str, + 'title' => __('Help'), + 'content' => $help_page + )); + } + } + + // Add help sidebar + if (isset($this->pages[$base_str]['page_help_sidebar']) && !empty($this->pages[$base_str]['page_help_sidebar'])) { + $screen->set_help_sidebar($this->pages[$base_str]['page_help_sidebar']); + } + } + + public function set_adminbar_menu($admin_bar) { + foreach ($this->pages as $id => $page) { + if (!$page['display_banner_menu']) { + continue; + } + $menu_details = array( + 'id' => $page['id'], + 'title' => $page['menu_name'], + 'href' => $page['url'], + 'meta' => array( + 'title' => $page['menu_name'] + ) + ); + if (isset($page['parent']) && array_key_exists($page['parent'], $this->pages)) { + $menu_details['parent'] = $this->prefix . $page['parent']; + } + $admin_bar->add_menu($menu_details); + } + } + + public function add_settings_link($links) { + foreach ($this->pages as $id => $page) { + if (!$page['settings_link']) { + continue; + } + $settings_link = '' . $page['settings_name'] . ''; + array_unshift($links, $settings_link); + } + return $links; + } + + public function set_admin_page_main() { + $page = $this->get_page(); + + echo $this->get_wrapper_start(); + + // Default Form + if ($this->pages[$page]['has_form']): + echo '
pages[$page]['has_file'] ? ' enctype="multipart/form-data"' : '') . '>
'; + echo ''; + echo ''; + wp_nonce_field('action-main-form-' . $page, 'action-main-form-' . $this->options['id'] . '-' . $page); + endif; + call_user_func($this->pages[$page]['function_content']); + if ($this->pages[$page]['has_form']): + echo '
'; + endif; + + echo $this->get_wrapper_end(); + } + + public function set_admin_page_main_postAction() { + $page = $this->get_page(); + $action_id = 'action-main-form-' . $this->options['id'] . '-' . $page; + if (empty($_POST) || !isset($_POST[$action_id]) || !wp_verify_nonce($_POST[$action_id], 'action-main-form-' . $page)) { + return; + } + call_user_func($this->pages[$page]['function_action']); + wp_redirect($this->pages[$page]['url']); + } + + private function get_wrapper_start() { + return '

' . get_admin_page_title() . '


'; + } + + private function get_wrapper_end() { + return '
'; + } + + private function get_page() { + $page = ''; + if (isset($_GET['page'])) { + $page = str_replace($this->options['id'] . '-', '', $_GET['page']); + } + if (isset($_POST['page_name'])) { + $page = str_replace($this->options['id'] . '-', '', $_POST['page_name']); + } + return $page; + } +} diff --git a/inc/WPUBaseCron/README.md b/inc/WPUBaseCron/README.md new file mode 100644 index 0000000..5b35b4e --- /dev/null +++ b/inc/WPUBaseCron/README.md @@ -0,0 +1,25 @@ +WPU Base Cron +--- + +Add Cron to your plugin. + +## Insert in the plugins_loaded hook + +```php +include 'inc/WPUBaseCron.php'; +$this->basecron = new \wpubaseplugin\WPUBaseCron(array( + 'pluginname' => 'Base Plugin', // Default : [Namespace] + 'cronhook' => 'wpubaseplugin__cron_hook', // Default : [namespace__cron_hook] + 'croninterval' => 900 // Default : [3600] +)); +/* Callback when hook is triggered by the cron */ +add_action('wpubaseplugin__cron_hook', array(&$this, + 'wpubaseplugin__callback_function' +), 10); +``` + +## uninstall hook ## + +```php +$this->basecron->uninstall(); +``` diff --git a/inc/WPUBaseCron/WPUBaseCron.php b/inc/WPUBaseCron/WPUBaseCron.php new file mode 100644 index 0000000..3eb0265 --- /dev/null +++ b/inc/WPUBaseCron/WPUBaseCron.php @@ -0,0 +1,160 @@ +ns = preg_replace('/\W+/', '', __NAMESPACE__); + + /* Settings */ + $this->pluginname = isset($settings['pluginname']) ? $settings['pluginname'] : ucfirst($this->ns); + $this->cronhook = isset($settings['cronhook']) ? $settings['cronhook'] : $this->ns . '__cron_hook'; + $this->croninterval = isset($settings['croninterval']) ? $settings['croninterval'] : 3600; + + /* Internal values */ + $this->cronoption = $this->cronhook . '_croninterval'; + $this->cronschedule = $this->cronhook . '_schedule'; + $this->cronlastexec = $this->cronhook . '_lastexec'; + + /* Hooks */ + add_filter('cron_schedules', array(&$this, + 'add_schedule' + )); + add_action($this->cronhook, array(&$this, + 'store_execution_time' + ), 99); + + /* Check cron */ + add_action('wp', array(&$this, + 'check_cron' + )); + add_action('admin_init', array(&$this, + 'check_cron' + )); + } + + /* Create schedule */ + public function add_schedule($schedules) { + $schedules[$this->cronschedule] = array( + 'interval' => $this->croninterval, + 'display' => $this->pluginname . ' - Custom' + ); + return $schedules; + } + + /* Schedule cron if possible */ + public function check_cron() { + $croninterval = intval(get_option($this->cronoption), 10); + $schedule = wp_next_scheduled($this->cronhook); + + // If no schedule cron or new interval or incorrect interval + if (!$schedule || $croninterval != $this->croninterval) { + $this->install(); + return; + } + + // Schedule is too in the future + if ($schedule - time() - $croninterval > 0) { + do_action($this->cronhook); + $this->install(); + return; + } + + // Schedule is too in the past + if ($schedule - time() < $croninterval * 12 * -1) { + do_action($this->cronhook); + $this->install(); + } + } + + /* ---------------------------------------------------------- + Time + ---------------------------------------------------------- */ + + /* Get time */ + public function get_time_details($schedule, $delta) { + if (!is_numeric($schedule) || !is_numeric($delta)) { + return false; + } + $minutes = 0; + $seconds = abs($delta); + if ($seconds >= 60) { + $minutes = (int) ($seconds / 60); + $seconds = $seconds % 60; + } + return array( + 'timestamp' => $schedule, + 'delta' => $delta, + 'min' => $minutes, + 'sec' => $seconds + ); + } + + /* Get next scheduled */ + public function get_next_scheduled() { + $schedule = wp_next_scheduled($this->cronhook); + return $this->get_time_details($schedule, $schedule - time()); + } + + /* Get previous execution */ + public function get_previous_exec() { + $schedule = get_option($this->cronlastexec); + return $this->get_time_details($schedule, time() - $schedule); + } + + public function store_execution_time() { + update_option($this->cronlastexec, time()); + } + + /* ---------------------------------------------------------- + Activation + ---------------------------------------------------------- */ + + /* Create cron */ + public function install() { + wp_clear_scheduled_hook($this->cronhook); + update_option($this->cronoption, $this->croninterval); + wp_schedule_event(time() + $this->croninterval, $this->cronschedule, $this->cronhook); + } + + /* Destroy cron */ + public function uninstall() { + wp_clear_scheduled_hook($this->cronhook); + delete_option($this->cronlastexec); + delete_option($this->cronoption); + flush_rewrite_rules(); + } +} + +/* + ## plugins_loaded ## + include 'inc/WPUBaseCron.php'; + $WPUBaseCron = new WPUBaseCron(array( + 'pluginname' => 'Base Plugin', + 'cronhook' => 'wpubaseplugin__cron_hook', + 'croninterval' => 900 + )); + + ## uninstall hook ## + $WPUBaseCron->uninstall(); + * + */ diff --git a/inc/WPUBaseEmail/README.md b/inc/WPUBaseEmail/README.md new file mode 100644 index 0000000..a88b36f --- /dev/null +++ b/inc/WPUBaseEmail/README.md @@ -0,0 +1,17 @@ +WPU Base Email +--- + +Nicely formatted emails in your plugin. + +## Insert in the INIT hook + +```php +require_once dirname(__FILE__) . '/inc/WPUBaseEmail/WPUBaseEmail.php'; +$this->baseemail = new \wpu_polls\WPUBaseEmail(); +``` + +## Send an email where you need it + +```php +$this->baseemail->send_email('test subject', 'test content'); +`` diff --git a/inc/WPUBaseEmail/WPUBaseEmail.php b/inc/WPUBaseEmail/WPUBaseEmail.php new file mode 100644 index 0000000..9d9667c --- /dev/null +++ b/inc/WPUBaseEmail/WPUBaseEmail.php @@ -0,0 +1,44 @@ + + + + .link-nostyle { color: inherit; } + + + + + + + + + + + + + Hello World + + Hello World + + + + diff --git a/inc/WPUBaseEmail/templates/template.php b/inc/WPUBaseEmail/templates/template.php new file mode 100644 index 0000000..2bbf75d --- /dev/null +++ b/inc/WPUBaseEmail/templates/template.php @@ -0,0 +1,52 @@ +

diff --git a/inc/WPUBaseSettings/README.md b/inc/WPUBaseSettings/README.md new file mode 100644 index 0000000..2103da3 --- /dev/null +++ b/inc/WPUBaseSettings/README.md @@ -0,0 +1,45 @@ +WPU Base Settings +--- + +Add settings in your plugin. + +## Insert in the INIT hook + +```php +$this->settings_details = array( + # Admin page + 'create_page' => true, + 'plugin_basename' => plugin_basename(__FILE__), + # Default + 'plugin_name' => 'Import Twitter', + 'plugin_id' => 'wpuimporttwitter', + 'option_id' => 'wpuimporttwitter_options', + 'sections' => array( + 'import' => array( + 'name' => __('Import Settings', 'wpuimporttwitter') + ) + ) +); +$this->settings = array( + 'sources' => array( + 'label' => __('Sources', 'wpuimporttwitter'), + 'help' => __('One #hashtag or one @user per line.', 'wpuimporttwitter'), + 'type' => 'textarea' + ) +); +if (is_admin()) { + include dirname( __FILE__ ) . '/inc/WPUBaseSettings/WPUBaseSettings.php'; + new \wpuimporttwitter\WPUBaseSettings($this->settings_details,$this->settings); +} +``` + +## Insert in your admin page content ( if needed ) + +```php +settings_errors(); +echo '
'; +settings_fields($this->settings_details['option_id']); +do_settings_sections($this->options['plugin_id']); +echo submit_button(__('Save Changes', 'wpuimporttwitter')); +echo '
'; +`` diff --git a/inc/WPUBaseSettings/WPUBaseSettings.php b/inc/WPUBaseSettings/WPUBaseSettings.php new file mode 100644 index 0000000..ac6cccc --- /dev/null +++ b/inc/WPUBaseSettings/WPUBaseSettings.php @@ -0,0 +1,722 @@ +init($settings_details, $settings); + } + + public function init($settings_details = array(), $settings = array()) { + if (empty($settings_details) || empty($settings)) { + return; + } + $this->set_datas($settings_details, $settings); + $this->has_media_setting = false; + foreach ($this->settings as $setting) { + if ($setting['type'] == 'media') { + $this->has_media_setting = true; + } + } + + $this->is_admin_page = isset($_GET['page']) && $_GET['page'] == $this->settings_details['plugin_id']; + $this->has_create_page = isset($settings_details['create_page']) && $settings_details['create_page']; + + $opt = $this->get_settings(); + add_action('admin_init', array(&$this, + 'add_settings' + )); + add_filter('option_page_capability_' . $this->settings_details['option_id'], array(&$this, + 'set_min_capability' + )); + add_action('admin_notices', array(&$this, + 'admin_notices' + )); + if ($this->has_create_page) { + add_action('admin_menu', array(&$this, + 'admin_menu' + )); + $this->admin_url = admin_url($this->settings_details['parent_page_url'] . '?page=' . $this->settings_details['plugin_id']); + if (isset($settings_details['plugin_basename'])) { + add_filter("plugin_action_links_" . $settings_details['plugin_basename'], array(&$this, 'plugin_add_settings_link')); + } + } else { + add_action('init', array(&$this, 'load_assets')); + } + } + + public function admin_notices() { + if (!$this->is_admin_page) { + return; + } + if ($this->settings_details['parent_page'] == 'options-general.php') { + return; + } + settings_errors(); + } + + public function get_settings() { + return $this->get_setting_values(); + } + + public function get_setting($id, $lang = false) { + $opt = $this->get_settings(); + if ($lang === true) { + $id = $this->get_current_language() . '__' . $id; + } + if (isset($opt[$id])) { + return $opt[$id]; + } + return false; + } + + public function update_setting($id, $value) { + $opt = $this->get_settings(); + $opt[$id] = $value; + update_option($this->settings_details['option_id'], $opt); + } + + public function set_min_capability() { + return $this->settings_details['user_cap']; + } + + public function set_datas($settings_details, $settings) { + if (!is_array($settings_details)) { + $settings_details = array(); + } + if (!isset($settings_details['plugin_id'])) { + $settings_details['plugin_id'] = 'wpubasesettingsdefault'; + } + if (!isset($settings_details['user_cap'])) { + $settings_details['user_cap'] = 'manage_options'; + } + if (!isset($settings_details['option_id'])) { + $settings_details['option_id'] = $settings_details['plugin_id'] . '_options'; + } + if (!isset($settings_details['parent_page'])) { + $settings_details['parent_page'] = 'options-general.php'; + } + if (!isset($settings_details['parent_page_url'])) { + $settings_details['parent_page_url'] = $settings_details['parent_page']; + } + if (!isset($settings_details['plugin_name'])) { + $settings_details['plugin_name'] = $settings_details['plugin_id']; + } + if (!isset($settings_details['show_in_rest'])) { + $settings_details['show_in_rest'] = false; + } + if (!isset($settings_details['sections']) || empty($settings_details['sections'])) { + $settings_details['sections'] = array( + 'default' => array( + 'name' => __('Settings') + ) + ); + } + foreach ($settings_details['sections'] as $id => $section) { + if (!isset($section['user_cap'])) { + $settings_details['sections'][$id]['user_cap'] = $settings_details['user_cap']; + } + } + $this->settings_details = $settings_details; + if (!is_array($settings)) { + $settings = array( + 'option_example' => array( + 'label' => 'My label', + 'help' => 'My help', + 'type' => 'textarea' + ) + ); + } + + $default_section = key($this->settings_details['sections']); + foreach ($settings as $id => $input) { + $settings[$id]['required'] = isset($input['required']) ? $input['required'] : false; + $settings[$id]['default_value'] = isset($input['default_value']) ? $input['default_value'] : ''; + $settings[$id]['label'] = isset($input['label']) ? $input['label'] : ''; + $settings[$id]['label_check'] = isset($input['label_check']) ? $input['label_check'] : $settings[$id]['label']; + $settings[$id]['help'] = isset($input['help']) ? $input['help'] : ''; + $settings[$id]['type'] = isset($input['type']) ? $input['type'] : 'text'; + $settings[$id]['post_type'] = isset($input['post_type']) ? $input['post_type'] : 'post'; + $settings[$id]['section'] = isset($input['section']) ? $input['section'] : $default_section; + $settings[$id]['datas'] = isset($input['datas']) && is_array($input['datas']) ? $input['datas'] : array(__('No'), __('Yes')); + $settings[$id]['editor_args'] = isset($input['editor_args']) && is_array($input['editor_args']) ? $input['editor_args'] : array(); + $settings[$id]['user_cap'] = $this->settings_details['sections'][$settings[$id]['section']]['user_cap']; + } + + $languages = $this->get_languages(); + + /* Set multilingual fields */ + $new_settings = array(); + foreach ($settings as $id => $input) { + if (!isset($input['lang']) || empty($languages)) { + $new_settings[$id] = $input; + continue; + } + foreach ($languages as $lang => $lang_name) { + $input_lang = $input; + unset($input_lang['lang']); + $input_lang['translated_from'] = $id; + $input_lang['lang_id'] = $lang; + $input_lang['label'] = '[' . $lang . '] ' . $input_lang['label']; + $new_settings[$lang . '__' . $id] = $input_lang; + } + + } + + $this->settings = $new_settings; + } + + public function add_settings() { + register_setting($this->settings_details['option_id'], $this->settings_details['option_id'], array( + 'sanitize_callback' => array(&$this, 'options_validate'), + 'show_in_rest' => $this->settings_details, + 'default' => array() + )); + foreach ($this->settings_details['sections'] as $id => $section) { + if (current_user_can($section['user_cap'])) { + add_settings_section($id, + $section['name'], + isset($section['description']) ? $section['description'] : '', + $this->settings_details['plugin_id'] + ); + } + } + + foreach ($this->settings as $id => $input) { + // Hide input if not in capacity + if (!current_user_can($input['user_cap'])) { + continue; + } + $lang_id = ''; + if (isset($input['lang_id'])) { + $lang_id = $input['lang_id']; + } + add_settings_field($id, $this->settings[$id]['label'], array(&$this, + 'render__field' + ), $this->settings_details['plugin_id'], $this->settings[$id]['section'], array( + 'name' => $this->settings_details['option_id'] . '[' . $id . ']', + 'id' => $id, + 'lang_id' => $lang_id, + 'label_for' => $id, + 'translated_from' => isset($this->settings[$id]['translated_from']) ? $this->settings[$id]['translated_from'] : false, + 'required' => $this->settings[$id]['required'], + 'post_type' => $this->settings[$id]['post_type'], + 'datas' => $this->settings[$id]['datas'], + 'type' => $this->settings[$id]['type'], + 'help' => $this->settings[$id]['help'], + 'default_value' => $this->settings[$id]['default_value'], + 'editor_args' => $this->settings[$id]['editor_args'], + 'label_check' => $this->settings[$id]['label_check'] + )); + } + } + + public function options_validate($input) { + $options = get_option($this->settings_details['option_id']); + foreach ($this->settings as $id => $setting) { + + // If regex : use it to validate the field + if (isset($setting['regex'])) { + if (isset($input[$id]) && preg_match($setting['regex'], $input[$id])) { + $options[$id] = $input[$id]; + } else { + if (isset($setting['default'])) { + $options[$id] = $setting['default']; + } + } + continue; + } + + // Set a default value + if ($setting['type'] != 'checkbox') { + // - if not sent or if user is not allowed + if (!isset($input[$id]) || !current_user_can($setting['user_cap'])) { + $input[$id] = isset($options[$id]) ? $options[$id] : '0'; + } + $option_id = $input[$id]; + } + switch ($setting['type']) { + case 'checkbox': + $option_id = isset($input[$id]) && !in_array($input[$id], array('0', '')) ? '1' : '0'; + break; + case 'radio': + case 'select': + if (!array_key_exists($input[$id], $setting['datas'])) { + $option_id = key($setting['datas']); + } + break; + case 'email': + if (filter_var($input[$id], FILTER_VALIDATE_EMAIL) === false) { + $option_id = ''; + } + break; + case 'url': + if (filter_var($input[$id], FILTER_VALIDATE_URL) === false) { + $option_id = ''; + } + break; + case 'post': + case 'page': + case 'media': + case 'number': + if (!is_numeric($input[$id])) { + $option_id = 0; + } + break; + case 'editor': + $option_id = trim($input[$id]); + break; + default: + $option_id = esc_html(trim($input[$id])); + } + + $options[$id] = $option_id; + } + + add_settings_error( + $this->settings_details['option_id'], + $this->settings_details['option_id'] . esc_attr('settings_updated'), + __('Settings saved.'), + 'updated' + ); + + return $options; + } + + public function render__field($args = array()) { + $option_id = $this->settings_details['option_id']; + $options = get_option($option_id); + $name_val = $option_id . '[' . $args['id'] . ']'; + $name = ' name="' . $name_val . '" '; + $id = ' id="' . $args['id'] . '" '; + $attr = ''; + if (isset($args['lang_id']) && $args['lang_id']) { + $attr .= ' data-wpulang="' . esc_attr($args['lang_id']) . '" '; + } + if (isset($args['required']) && $args['required']) { + $attr .= ' required="required" '; + } + if (isset($args['placeholder']) && $args['placeholder']) { + $attr .= ' placeholder="' . esc_attr($args['placeholder']) . '" '; + } + if (isset($args['attributes_html']) && $args['attributes_html']) { + $attr .= ' ' . $args['attributes_html']; + } + $id .= $attr; + $value = isset($options[$args['id']]) ? $options[$args['id']] : $args['default_value']; + if(!isset($options[$args['id']]) && isset($args['translated_from']) && $args['translated_from'] && isset($options[$args['translated_from']]) && $options[$args['translated_from']]){ + $value = $options[$args['translated_from']]; + } + + switch ($args['type']) { + case 'checkbox': + $checked_val = isset($options[$args['id']]) ? $options[$args['id']] : '0'; + echo ''; + break; + case 'textarea': + echo ''; + break; + case 'media': + $img_src = ''; + if (is_numeric($value)) { + $tmp_src = wp_get_attachment_image_src($value, 'medium'); + if (is_array($tmp_src)) { + $img_src = ' src="' . esc_attr($tmp_src[0]) . '" '; + } + } + echo '
'; + echo ''; + /* Preview */ + echo '
'; + echo '×'; + echo ''; + echo '
'; + echo ''; + echo '
'; + break; + case 'radio': + foreach ($args['datas'] as $_id => $_data) { + echo '

'; + echo ''; + echo ''; + echo '

'; + } + break; + case 'post': + case 'page': + $code_dropdown = wp_dropdown_pages(array( + 'echo' => false, + 'name' => $name_val, + 'id' => $args['id'], + 'selected' => $value, + 'post_type' => isset($args['post_type']) ? $args['post_type'] : $args['type'] + )); + echo str_replace(''; + break; + case 'editor': + $editor_args = array( + 'textarea_rows' => isset($args['textarea_rows']) && is_numeric($args['textarea_rows']) ? $args['textarea_rows'] : 3 + ); + if (isset($args['editor_args']) && is_array($args['editor_args'])) { + $editor_args = $args['editor_args']; + } + $editor_args['textarea_name'] = $name_val; + wp_editor($value, $option_id . '_' . $args['id'], $editor_args); + echo ''; + break; + case 'url': + case 'number': + case 'email': + case 'text': + echo ''; + } + if (!empty($args['help'])) { + echo '
' . $args['help'] . '
'; + } + } + + public static function isRegex($str0) { + /* Thx https://stackoverflow.com/a/16098097 */ + $regex = "/^\/[\s\S]+\/$/"; + return preg_match($regex, $str0); + } + + /* Media */ + public function load_assets() { + add_action('admin_footer', array(&$this, 'admin_footer')); + + if (!$this->has_media_setting) { + return; + } + + add_action('admin_print_scripts', array(&$this, 'admin_scripts')); + add_action('admin_print_styles', array(&$this, 'admin_styles')); + add_action('admin_head', array(&$this, 'admin_head')); + add_action('admin_footer', array(&$this, 'admin_footer_medias')); + } + + public function admin_scripts() { + wp_enqueue_script('media-upload'); + wp_enqueue_media(); + } + + public function admin_styles() { + wp_enqueue_style('thickbox'); + } + + public function admin_head() { + echo << +.wpubasesettings-mediabox .img-preview { + z-index: 1; + position: relative; +} + +.wpubasesettings-mediabox .img-preview img { + max-width: 100px; +} + +.wpubasesettings-mediabox .img-preview .x { + z-index: 1; + position: absolute; + top: 0; + left: 0; + padding: 0.2em; + text-decoration: none; + font-weight: bold; + line-height: 1; + color: #000; + background-color: #fff; +} + +EOT; + } + + public function admin_footer_medias() { + echo << +/* Delete image */ +jQuery('.wpubasesettings-mediabox .x').click(function(e) { + var \$this = jQuery(this), + \$parent = \$this.closest('.wpubasesettings-mediabox'), + \$imgPreview = \$parent.find('.img-preview'); + \$imgField = \$parent.find('input[type="hidden"]'); + e.preventDefault(); + \$imgPreview.css({'display':'none'}); + \$imgField.val(''); +}); + +/* Add image */ +jQuery('.wpubasesettings-mediabox .button').click(function(e) { + var \$this = jQuery(this), + \$parent = \$this.closest('.wpubasesettings-mediabox'), + \$imgPreview = \$parent.find('.img-preview'); + \$imgField = \$parent.find('input[type="hidden"]'); + + var frame = wp.media({multiple: false }); + + // When an image is selected in the media frame... + frame.on('select', function() { + var attachment = frame.state().get('selection').first().toJSON(); + \$imgPreview.css({'display':'block'}); + \$imgPreview.find('img').attr('src',attachment.url); + // Send the attachment id to our hidden input + \$imgField.val(attachment.id); + }); + + // Finally, open the modal on click + frame.open(); + + e.preventDefault(); +}); + + +EOT; + } + + public function admin_footer() { + $option_id = $this->settings_details['option_id']; + $languages = json_encode($this->get_languages()); + $label_txt = __('Language'); + echo << +(function(){ +/* Check langs */ +var _langs = {$languages}; +if(!_langs){ + return; +} + +/* Get items */ +var jQinput = jQuery('input[type="hidden"][name="option_page"][value="{$option_id}"]'); +if(!jQinput.length){ + return; +} +var jQform = jQinput.closest('form'); + +/* Add lang on TR */ +jQform.find('[data-wpulang]').each(function(i,el){ + var jQel = jQuery(el); + jQel.closest('tr').attr('data-wpulangtr', jQel.attr('data-wpulang')); +}); +var jQTr = jQform.find('[data-wpulangtr]'), + _firstLang = Object.keys(_langs)[0]; +if(!jQTr.length){ + return; +} + +/* Build switch */ +var select_html=''; +for(var _l in _langs){ + select_html+=''; +} +var jQSelect = jQuery(''); +jQSelect.prependTo(jQform); + +/* Switch */ +function show_lang(_lang_id){ + jQTr.hide(); + jQTr.filter('[data-wpulangtr="'+_lang_id+'"]').show(); +} +show_lang(_firstLang); +jQSelect.on('change', 'select',function(){ + show_lang(jQuery(this).val()); +}); + +}()); + +EOT; + } + + /* Base settings */ + + public function admin_menu() { + $this->hook_page = add_submenu_page($this->settings_details['parent_page'], $this->settings_details['plugin_name'] . ' - ' . __('Settings'), $this->settings_details['plugin_name'], $this->settings_details['user_cap'], $this->settings_details['plugin_id'], array(&$this, + 'admin_settings' + ), 110); + add_action('load-' . $this->hook_page, array(&$this, 'load_assets')); + } + + public function plugin_add_settings_link($links) { + $settings_link = '' . __('Settings') . ''; + array_push($links, $settings_link); + return $links; + } + + public function admin_settings() { + echo '
'; + do_action('wpubasesettings_after_wrap_start' . $this->hook_page); + echo apply_filters('wpubasesettings_page_title_' . $this->hook_page, '

' . get_admin_page_title() . '

'); + do_action('wpubasesettings_before_content_' . $this->hook_page); + if (current_user_can($this->settings_details['user_cap'])) { + echo apply_filters('wpubasesettings_before_form_' . $this->hook_page, '
'); + echo '
'; + settings_fields($this->settings_details['option_id']); + do_settings_sections($this->settings_details['plugin_id']); + echo submit_button(__('Save')); + echo '
'; + } + do_action('wpubasesettings_after_content_' . $this->hook_page); + do_action('wpubasesettings_before_wrap_end' . $this->hook_page); + echo '
'; + } + + /* Get settings */ + + public function get_setting_values($lang = false) { + if (!isset($this->settings) || !is_array($this->settings)) { + return array(); + } + if (!$lang) { + $lang = $this->get_current_language(); + } + $settings = get_option($this->settings_details['option_id']); + if (!is_array($settings)) { + $settings = array(); + } + foreach ($this->settings as $key => $setting) { + /* Default fields */ + if (!isset($settings[$key]) && !isset($setting['translated_from'])) { + $default_value = false; + if (isset($this->settings[$key], $this->settings[$key]['default'])) { + $default_value = $this->settings[$key]['default']; + } + $settings[$key] = $default_value; + } + if (isset($setting['translated_from'], $setting['lang_id'], $settings[$key]) && $lang == $setting['lang_id'] && $settings[$key] !== false) { + $settings[$setting['translated_from']] = $settings[$key]; + } + } + return $settings; + } + + public function get_languages() { + // Obtaining from Qtranslate + if (function_exists('qtrans_getSortedLanguages')) { + return qtrans_getSortedLanguages(); + } + + // Obtaining from Qtranslate X + if (function_exists('qtranxf_getSortedLanguages')) { + return qtranxf_getSortedLanguages(); + } + + // Obtaining from Polylang + global $polylang; + if (function_exists('pll_the_languages') && is_object($polylang)) { + $poly_langs = $polylang->model->get_languages_list(); + $languages = array(); + foreach ($poly_langs as $lang) { + $languages[$lang->slug] = $lang->slug; + } + return $languages; + } + + // Obtaining from WPML + if (function_exists('icl_get_languages')) { + $wpml_lang = icl_get_languages(); + foreach ($wpml_lang as $lang) { + $languages[$lang['code']] = $lang['native_name']; + } + return $languages; + } + return array(); + + } + + public function get_current_language() { + + // Obtaining from Qtranslate + if (function_exists('qtrans_getLanguage')) { + return qtrans_getLanguage(); + } + + // Obtaining from Qtranslate X + if (function_exists('qtranxf_getLanguage')) { + return qtranxf_getLanguage(); + } + + // Obtaining from Polylang + if (function_exists('pll_current_language')) { + return pll_current_language(); + } + + // Obtaining from WPML + if (defined('ICL_LANGUAGE_CODE')) { + return ICL_LANGUAGE_CODE; + } + + return ''; + } + +} + +/* + ## INIT ## + $this->settings_details = array( + # Create admin page + 'create_page' => true, + 'parent_page' => 'tools.php', + 'plugin_name' => 'Maps Autocomplete', + # Default + 'plugin_id' => 'wpuimporttwitter', + 'user_cap' => 'manage_options', + 'option_id' => 'wpuimporttwitter_options', + 'sections' => array( + 'import' => array( + 'name' => __('Import Settings', 'wpuimporttwitter') + ) + ) + ); + $this->settings = array( + 'sources' => array( + 'label' => __('Sources', 'wpuimporttwitter'), + 'help' => __('One #hashtag or one @user per line.', 'wpuimporttwitter'), + 'type' => 'textarea' + ) + ); + if (is_admin()) { + include 'inc/WPUBaseSettings.php'; + $settings_obj = new \wpuimporttwitter\WPUBaseSettings($this->settings_details, $this->settings); + + ## if no auto create_page and medias ## + if(isset($_GET['page']) && $_GET['page'] == 'wpuimporttwitter'){ + add_action('admin_init', array(&$settings_obj, 'load_assets')); + } + } + + ## IN ADMIN PAGE if no auto create_page ## + echo '
'; + settings_fields($this->settings_details['option_id']); + do_settings_sections($this->options['plugin_id']); + echo submit_button(__('Save Changes', 'wpuimporttwitter')); + echo '
'; +*/ diff --git a/inc/WPUBaseToolbox/README.md b/inc/WPUBaseToolbox/README.md new file mode 100644 index 0000000..e5fcb9f --- /dev/null +++ b/inc/WPUBaseToolbox/README.md @@ -0,0 +1,35 @@ +WPU Base Toolbox +--- + +Cool helpers for WordPress Plugins. + +## Insert in the INIT hook + +```php +require_once dirname(__FILE__) . '/inc/WPUBaseToolbox/WPUBaseToolbox.php'; +$this->basetoolbox = new \myplugin\WPUBaseToolbox(); +``` + +## Use functions + + +### Get form HTML + +```php +echo $this->basetoolbox->get_form_html('form_edit_note', array( + 'note_name' => array( + 'label' => __('Note Name', 'myplugin'), + 'value' => 'base value' + ), + 'note_content' => array( + 'label' => __('Note Content', 'myplugin'), + 'type' => 'textarea' + ) +), array( + 'button_label' => __('Send modifications', 'myplugin'), + 'hidden_fields' => array( + 'method' => 'edit_note', + 'action' => 'myplugin_callback_crud' + ) +)); +``` diff --git a/inc/WPUBaseToolbox/WPUBaseToolbox.php b/inc/WPUBaseToolbox/WPUBaseToolbox.php new file mode 100644 index 0000000..5a6d10d --- /dev/null +++ b/inc/WPUBaseToolbox/WPUBaseToolbox.php @@ -0,0 +1,398 @@ +plugin_version); + } + + /* ---------------------------------------------------------- + Forms + ---------------------------------------------------------- */ + + /* Wrapper + -------------------------- */ + + public function get_form_html($form_id, $fields = array(), $args = array()) { + $html = ''; + if (!is_array($fields) || !is_array($args)) { + return ''; + } + + $args = $this->get_clean_form_args($form_id, $fields, $args); + + $extra_post_attributes = $args['form_attributes']; + + /* Clean & check fields */ + $has_file = false; + foreach ($fields as $field_name => $field) { + $fields[$field_name] = $this->get_clean_field($field_name, $field, $form_id, $args); + if ($fields[$field_name]['type'] == 'file') { + $has_file = true; + } + } + + /* Extra attributes */ + if ($has_file) { + $extra_post_attributes .= ' enctype="multipart/form-data"'; + } + + $button_submit = ''; + + /* Start form */ + $html .= '
'; + $html .= $args['html_before_content']; + + $html_fieldset = ''; + $html_wizard = ''; + + /* Insert fields */ + $nb_fieldsets = count($args['fieldsets']); + $fieldset_num = 0; + foreach ($args['fieldsets'] as $fieldset_id => $fieldset) { + $fieldset_num++; + + if ($args['wizard_steps'] && (!isset($fieldset['label']) || !$fieldset['label'])) { + $fieldset['label'] = $fieldset_id; + } + + $attributes = array_merge($fieldset['attributes'], array( + 'data-fielset-id' => $fieldset_id + )); + + $html_fieldset .= '
array_to_html_attributes($attributes) . '>'; + $html_fieldset .= $fieldset['content_before']; + if (isset($fieldset['label']) && $fieldset['label']) { + $html_fieldset .= '' . esc_html($fieldset['label']) . ''; + + if ($args['wizard_steps']) { + $html_wizard .= ''; + } + } + foreach ($fields as $field_name => $field) { + if ($field['fieldset'] != $fieldset_id) { + continue; + } + $html_fieldset .= $this->get_field_html($field_name, $field, $form_id, $args); + } + $html_fieldset .= $fieldset['content_after']; + if ($args['wizard_mode']) { + $btn_prev_class = isset($fieldset['wizard_prev_button_class']) && $fieldset['wizard_prev_button_class'] ? $fieldset['wizard_prev_button_class'] : $args['wizard_prev_button_class']; + $btn_next_class = isset($fieldset['wizard_next_button_class']) && $fieldset['wizard_next_button_class'] ? $fieldset['wizard_next_button_class'] : $args['wizard_next_button_class']; + $btn_prev_label = isset($fieldset['wizard_prev_button_label']) && $fieldset['wizard_prev_button_label'] ? $fieldset['wizard_prev_button_label'] : $args['wizard_prev_button_label']; + $btn_next_label = isset($fieldset['wizard_next_button_label']) && $fieldset['wizard_next_button_label'] ? $fieldset['wizard_next_button_label'] : $args['wizard_next_button_label']; + + $html_fieldset .= '
'; + if ($fieldset_num > 1) { + $html_fieldset .= ''; + } + if ($fieldset_num == $nb_fieldsets) { + $html_fieldset .= $button_submit; + } else { + $html_fieldset .= ''; + } + $html_fieldset .= '
'; + } + $html_fieldset .= '
'; + } + + if ($html_wizard) { + $html .= '
'; + $html .= $html_wizard; + $html .= '
'; + } + $html .= $html_fieldset; + + /* Submit box */ + $html .= '
'; + foreach ($args['hidden_fields'] as $field_id => $field_value) { + $html .= ''; + } + if ($args['has_nonce']) { + $html .= wp_nonce_field($args['nonce_id'], $args['nonce_name'], 0, 0); + } + if (!$args['wizard_mode']) { + $html .= $button_submit; + } + $html .= '
'; + + $html .= $args['html_after_content']; + /* End form */ + $html .= '
'; + + return $html; + } + + /* Clean form value + -------------------------- */ + + public function get_clean_form_args($form_id, $fields = array(), $args = array()) { + $default_args = array( + 'button_label' => __('Submit'), + 'button_classname' => 'cssc-button', + 'fieldsets' => array( + 'default' => array( + 'label' => '', + 'attributes' => array(), + 'content_before' => '', + 'content_after' => '' + ) + ), + 'form_attributes' => '', + 'form_classname' => 'cssc-form', + 'field_group_classname' => 'twoboxes', + 'field_box_classname' => 'box', + 'submit_box_classname' => 'box--submit', + 'html_before_content' => '', + 'html_after_content' => '', + 'hidden_fields' => array(), + 'has_nonce' => true, + 'nonce_id' => $form_id, + 'nonce_name' => $form_id . '_nonce', + 'wizard_mode' => false, + 'wizard_steps' => false, + 'wizard_prev_button_class' => 'btn--prev', + 'wizard_next_button_class' => 'btn--next', + 'wizard_prev_button_label' => __('Previous'), + 'wizard_next_button_label' => __('Next') + ); + $args = array_merge($default_args, $args); + + $args = apply_filters('wpubasetoolbox_get_form_html_args_' . __NAMESPACE__, $args); + if (!is_array($args['hidden_fields']) || !isset($args['hidden_fields'])) { + $args['hidden_fields'] = array(); + } + if (!is_array($args['fieldsets']) || !isset($args['fieldsets'])) { + $args['fieldsets'] = array(); + } + foreach ($args['fieldsets'] as $fieldset_id => $fieldset) { + $args['fieldsets'][$fieldset_id] = array_merge($default_args['fieldsets']['default'], $fieldset); + if (!is_array($args['fieldsets'][$fieldset_id]['attributes'])) { + $args['fieldsets'][$fieldset_id]['attributes'] = array(); + } + } + return $args; + } + + /* Clean field values + -------------------------- */ + + public function get_clean_field($field_name, $field, $form_id, $args) { + if (!is_array($field)) { + $field = array(); + } + + if (!isset($field['fieldset']) || !array_key_exists($field['fieldset'], $args['fieldsets'])) { + $field['fieldset'] = array_key_first($args['fieldsets']); + } + + $default_field = array( + 'label' => $field_name, + 'type' => 'text', + 'fieldgroup_start' => false, + 'fieldgroup_end' => false, + 'html_before_fieldgroup' => '', + 'html_before_fieldgroup_inner' => '', + 'html_after_fieldgroup' => '', + 'html_after_fieldgroup_inner' => '', + 'html_before_content' => '', + 'html_after_content' => '', + 'value' => '', + 'extra_attributes' => '', + 'data_html' => '', + 'sub_fields' => array(), + 'data' => array( + '0' => __('No'), + '1' => __('Yes') + ), + 'required' => false + ); + $field = array_merge($default_field, $field); + + /* Ensure format */ + if (!is_array($field['data'])) { + $field['data'] = array(); + } + + if (!is_array($field['sub_fields'])) { + $field['sub_fields'] = array(); + } + + foreach ($field['sub_fields'] as $subfield_name => $sub_field) { + $field['sub_fields'][$subfield_name] = $this->get_clean_field($subfield_name, $sub_field, $form_id, $args); + } + + return $field; + } + + public function get_field_html($field_name, $field, $form_id, $args = array()) { + + if (!isset($field['extra_attributes'])) { + echo wp_debug_backtrace_summary(); + die; + } + + /* Values */ + $field_id = strtolower($form_id . '__' . $field_name); + $field_id_name = ' name="' . esc_attr($field_name) . '" id="' . esc_attr($field_id) . '" ' . $field['extra_attributes']; + if ($field['required']) { + $field_id_name .= ' required'; + } + + /* Label */ + $default_label = ''; + + /* Content */ + $html = ''; + switch ($field['type']) { + case 'textarea': + $html .= $default_label; + $html .= ''; + break; + + case 'select': + $html .= $default_label; + $html .= ''; + break; + + case 'radio': + $html .= $default_label; + foreach ($field['data'] as $key => $var) { + $id_field = strtolower($field_id . '___' . $key); + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + break; + + case 'checkbox': + $checked = $field['value'] ? ' checked="checked"' : ''; + $html .= ''; + $html .= $default_label; + break; + + case 'group': + foreach ($field['sub_fields'] as $subfield_name => $sub_field) { + $html .= $this->get_field_html($subfield_name, $sub_field, $form_id, $args); + } + break; + + default: + $html .= $default_label; + $html .= ''; + } + + if ($html) { + $field_html = $html; + $field_tag = ($field['type'] == 'group') ? 'div' : 'p'; + $html = ''; + $html .= $field['html_before_fieldgroup']; + if ($field['fieldgroup_start']) { + $html .= '
'; + } + $html .= $field['html_before_fieldgroup_inner']; + $html .= '<' . $field_tag . ' class="' . $args['field_box_classname'] . '" data-box-name="' . $field_name . '" data-box-type="' . esc_attr($field['type']) . '">'; + $html .= $field['html_before_content']; + $html .= $field_html; + $html .= ''; + $html .= $field['html_after_content']; + $html .= ''; + $html .= $field['html_after_fieldgroup_inner']; + if ($field['fieldgroup_end']) { + $html .= '
'; + } + $html .= $field['html_after_fieldgroup']; + } + + return $html; + } + + /* Validate form + -------------------------- */ + + function validate_form($source, $form_id, $fields = array(), $args = array()) { + if (!is_array($source) || empty($source) || empty($fields)) { + return false; + } + + $errors = array(); + + $args = $this->get_clean_form_args($form_id, $fields, $args); + + /* Check nonce */ + if ($args['has_nonce']) { + if (!isset($_POST[$args['nonce_name']]) || !wp_verify_nonce($_POST[$args['nonce_name']], $args['nonce_id'])) { + wp_nonce_ays(''); + } + } + + /* Check required fields */ + foreach ($fields as $field_name => $field) { + $field = $this->get_clean_field($field_name, $field, $form_id, $args); + $value = isset($source[$field_name]) ? $source[$field_name] : false; + + if ($field['required'] && !isset($source[$field_name])) { + $errors[] = sprintf(__('The field “%s” is required', __NAMESPACE__), $field['label']); + continue; + } + + if ($field['type'] == 'email' && $field['type'] && !is_email($value)) { + $errors[] = sprintf(__('The field “%s” should be an email', __NAMESPACE__), $field['label']); + } + } + + return $errors; + } + + /* ---------------------------------------------------------- + HTML Helpers + ---------------------------------------------------------- */ + + function array_to_html_attributes($attributes = array()) { + if (!is_array($attributes)) { + return ''; + } + + $html = ''; + foreach ($attributes as $key => $value) { + $html .= ' ' . $key . '="' . esc_attr($value) . '"'; + } + + return trim($html); + } + +} diff --git a/inc/WPUBaseToolbox/assets/form-validation.js b/inc/WPUBaseToolbox/assets/form-validation.js new file mode 100644 index 0000000..12885ca --- /dev/null +++ b/inc/WPUBaseToolbox/assets/form-validation.js @@ -0,0 +1,160 @@ +document.addEventListener("DOMContentLoaded", function() { + 'use strict'; + /* Boxes */ + Array.prototype.forEach.call(document.querySelectorAll('.wpubasetoolbox-form [data-box-name]'), wpubasetoolbox_box_validation); + + /* Wizard */ + Array.prototype.forEach.call(document.querySelectorAll('.wpubasetoolbox-form[data-wizard="1"]'), wpubasetoolbox_form_setup_wizard); +}); + +/* ---------------------------------------------------------- + Fieldset switch +---------------------------------------------------------- */ + +function wpubasetoolbox_form_setup_wizard($form) { + 'use strict'; + var $fieldsets = $form.querySelectorAll('fieldset'); + var _currentFieldset = 0; + var _nbFieldsets = $fieldsets.length; + var $wizardSteps = $form.querySelectorAll(' .form-wizard-steps [data-go]'); + + /* Display first fieldset */ + wpubasetoolbox_fieldset_display($fieldsets, _currentFieldset); + + /* On button click : change visible fieldset */ + Array.prototype.forEach.call($form.querySelectorAll(' .form-navigation [data-dir]'), function($btn) { + $btn.querySelector('span').style['pointer-events'] = 'none'; + $btn.addEventListener('click', btn_click_event, 1); + }); + Array.prototype.forEach.call($wizardSteps, function($btn) { + $btn.querySelector('span').style['pointer-events'] = 'none'; + $btn.addEventListener('click', btn_click_event_go, 1); + }); + + function btn_click_event(e) { + var _dir = e.target.getAttribute('data-dir'); + e.preventDefault(); + if (_dir == 'next') { + /* Check if a field is invalid in this fieldset*/ + if (wpubasetoolbox_fieldset_fieldset_has_invalid_fields($fieldsets[_currentFieldset])) { + return; + } + /* Allow next fieldset */ + _currentFieldset++; + } + else { + /* Always allow previous fieldset */ + _currentFieldset--; + } + go_to_fieldset(_currentFieldset); + } + + function btn_click_event_go(e) { + var _target_fieldset = parseInt(e.target.getAttribute('data-go'), 10); + e.preventDefault(); + for (var i = 0; i <= _target_fieldset; i++) { + go_to_fieldset(i); + /* Do not check target fieldset */ + if (i == _target_fieldset) { + break; + } + /* Check if a field is invalid in this fieldset */ + if (wpubasetoolbox_fieldset_fieldset_has_invalid_fields($fieldsets[i])) { + break; + } + } + } + + function go_to_fieldset(_fieldset) { + /* Ensure everything is ok */ + _currentFieldset = Math.max(0, _fieldset); + _currentFieldset = Math.min(_nbFieldsets - 1, _currentFieldset); + + $form.setAttribute('data-current-fieldset', _currentFieldset); + + /* Display fieldset */ + wpubasetoolbox_fieldset_display($fieldsets, _currentFieldset); + + /* Current wizard step */ + if ($wizardSteps.length) { + Array.prototype.forEach.call($wizardSteps, function(el) { + el.setAttribute('data-active', 0); + }); + $wizardSteps[_currentFieldset].setAttribute('data-active', 1); + } + + /* Event */ + $form.dispatchEvent(new CustomEvent("wpubasetoolbox_form_set_fieldset", { + detail: { + id: _currentFieldset, + item: $fieldsets[_currentFieldset] + } + })); + } +} + +function wpubasetoolbox_fieldset_fieldset_has_invalid_fields($fieldset) { + 'use strict'; + var $invalidFields = $fieldset.querySelectorAll(':invalid'); + Array.prototype.forEach.call($invalidFields, function(el) { + el.dispatchEvent(new Event('change')); + }); + return $invalidFields.length > 0; +} + +function wpubasetoolbox_fieldset_display($fieldsets, _nb) { + 'use strict'; + Array.prototype.forEach.call($fieldsets, function(el) { + el.style.display = 'none'; + }); + $fieldsets[_nb].style.display = ''; +} + +/* ---------------------------------------------------------- + Box validation +---------------------------------------------------------- */ + +function wpubasetoolbox_box_validation($box) { + 'use strict'; + var _id = $box.getAttribute('data-box-name'), + $fields = $box.querySelectorAll('[name="' + _id + '"]'), + $message = $box.querySelector('.wpubasetoolbox-form-validation-message'), + _ischecking = false; + + if (!$fields.length || !$message) { + return; + } + + function check_field_error($tmp_field) { + if (_ischecking) { + return false; + } + _ischecking = true; + var _valid = $tmp_field.checkValidity(); + _ischecking = false; + if (_valid) { + $box.setAttribute('data-box-error', '0'); + $message.innerHTML = ''; + return; + } + setTimeout(function() { + window.scrollTo({ + top: $box.getBoundingClientRect().top + window.pageYOffset - 100, + behavior: 'smooth' + }); + }, 10); + + $box.setAttribute('data-box-error', '1'); + $message.innerHTML = $tmp_field.validationMessage; + } + + Array.prototype.forEach.call($fields, function($tmp_field) { + $tmp_field.addEventListener("invalid", function() { + check_field_error($tmp_field); + }); + $tmp_field.addEventListener("change", function() { + check_field_error($tmp_field); + }); + }); + +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..2b6a61c --- /dev/null +++ b/index.php @@ -0,0 +1 @@ +query("DROP TABLE IF EXISTS {$wpdb->prefix}wpuerrorlogs_logs"); diff --git a/wpuerrorlogs.php b/wpuerrorlogs.php new file mode 100644 index 0000000..af4e7e4 --- /dev/null +++ b/wpuerrorlogs.php @@ -0,0 +1,322 @@ + 'wpuerrorlogs', + 'name' => 'WPU Error Logs' + ); + private $basetoolbox; + private $baseemail; + private $basecron; + private $adminpages; + private $baseadmindatas; + private $settings; + private $settings_obj; + private $settings_details; + private $plugin_description; + + public function __construct() { + add_action('plugins_loaded', array(&$this, 'plugins_loaded')); + } + + public function plugins_loaded() { + # TRANSLATION + if (!load_plugin_textdomain('wpuerrorlogs', false, dirname(plugin_basename(__FILE__)) . '/lang/')) { + load_muplugin_textdomain('wpuerrorlogs', dirname(plugin_basename(__FILE__)) . '/lang/'); + } + $this->plugin_description = __('Make sense of your log files', 'wpuerrorlogs'); + # TOOLBOX + require_once __DIR__ . '/inc/WPUBaseToolbox/WPUBaseToolbox.php'; + $this->basetoolbox = new \wpuerrorlogs\WPUBaseToolbox(); + # EMAIL + require_once __DIR__ . '/inc/WPUBaseEmail/WPUBaseEmail.php'; + $this->baseemail = new \wpuerrorlogs\WPUBaseEmail(); + # CUSTOM PAGE + $admin_pages = array( + 'main' => array( + 'icon_url' => 'dashicons-admin-generic', + 'menu_name' => $this->plugin_settings['name'], + 'name' => 'Main page', + 'settings_link' => true, + 'settings_name' => __('Settings'), + 'function_content' => array(&$this, + 'page_content__main' + ) + ) + ); + $pages_options = array( + 'id' => $this->plugin_settings['id'], + 'level' => 'manage_options', + 'basename' => plugin_basename(__FILE__) + ); + // Init admin page + require_once __DIR__ . '/inc/WPUBaseAdminPage/WPUBaseAdminPage.php'; + $this->adminpages = new \wpuerrorlogs\WPUBaseAdminPage(); + $this->adminpages->init($pages_options, $admin_pages); + # CUSTOM TABLE + require_once __DIR__ . '/inc/WPUBaseAdminDatas/WPUBaseAdminDatas.php'; + $this->baseadmindatas = new \wpuerrorlogs\WPUBaseAdminDatas(); + $this->baseadmindatas->init(array( + 'handle_database' => false, + 'plugin_id' => $this->plugin_settings['id'], + 'table_name' => 'wpuerrorlogs_logs', + 'table_fields' => array( + 'message' => array( + 'public_name' => 'message', + 'type' => 'sql', + 'sql' => 'MEDIUMTEXT' + ) + ) + )); + # SETTINGS + $this->settings_details = array( + # Admin page + 'create_page' => false, + 'plugin_basename' => plugin_basename(__FILE__), + # Default + 'plugin_name' => $this->plugin_settings['name'], + 'plugin_id' => $this->plugin_settings['id'], + 'option_id' => $this->plugin_settings['id'] . '_options', + 'sections' => array( + 'import' => array( + 'name' => __('Import Settings', 'wpuerrorlogs') + ) + ) + ); + $this->settings = array( + // 'value' => array( + // 'label' => __('My Value', 'wpuerrorlogs'), + // 'help' => __('A little help.', 'wpuerrorlogs'), + // 'type' => 'textarea' + // ) + ); + require_once __DIR__ . '/inc/WPUBaseSettings/WPUBaseSettings.php'; + $this->settings_obj = new \wpuerrorlogs\WPUBaseSettings($this->settings_details, $this->settings); + /* Include hooks */ + require_once __DIR__ . '/inc/WPUBaseCron/WPUBaseCron.php'; + $this->basecron = new \wpuerrorlogs\WPUBaseCron(array( + 'pluginname' => $this->plugin_settings['name'], + 'cronhook' => 'wpuerrorlogs__cron_hook', + 'croninterval' => 3600 + )); + /* Callback when hook is triggered by the cron */ + add_action('wpuerrorlogs__cron_hook', array(&$this, + 'wpuerrorlogs__cron_hook' + ), 10); + } + + public function wpuerrorlogs__cron_hook() { + + } + + public function page_content__main() { + + /* Find debug file */ + $logfile = ABSPATH . '/wp-content/debug.log'; + if (!WP_DEBUG_LOG) { + echo 'Debug logs are not enabled'; + return; + } + if (is_readable(WP_DEBUG_LOG)) { + $logfile = WP_DEBUG_LOG; + } + + $errors = $this->extract_logs_from_file($logfile); + + $textCounts = []; + + foreach ($errors as $error) { + if (!isset($textCounts[$error['text']])) { + $textCounts[$error['text']] = 0; + } + $textCounts[$error['text']]++; + } + arsort($textCounts); + + /* Keep only first five and extract data */ + $textCounts = array_slice($textCounts, 0, 5, true); + $display_values = array(); + foreach ($textCounts as $text => $count) { + $display_values[] = array( + 'count' => $count, + 'text' => $this->expand_error_text($text) + ); + } + + $colnames = array( + 'count' => __('Count', 'wpuerrorlogs'), + 'date' => __('Date', 'wpuerrorlogs'), + 'type' => __('Type', 'wpuerrorlogs'), + 'text' => __('Text', 'wpuerrorlogs') + ); + + echo '

' . __('Top errors', 'wpuerrorlogs') . '

'; + echo $this->array_to_html_table($display_values, array( + 'table_classname' => 'widefat', + 'colnames' => $colnames + )); + + $latest_errors = array_reverse($errors); + $latest_errors = array_slice($latest_errors, 0, 5, true); + echo '

' . __('Latest errors', 'wpuerrorlogs') . '

'; + echo $this->array_to_html_table($latest_errors, array( + 'table_classname' => 'widefat', + 'colnames' => $colnames + )); + + } + + /* ---------------------------------------------------------- + Extract logs from file + ---------------------------------------------------------- */ + + function extract_logs_from_file($file) { + $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $errors = []; + $currentError = array(); + + foreach ($lines as $line) { + + /* Is it a new error */ + if (substr($line, 0, 1) == '[' && preg_match('/^\[\d{2}-[A-Za-z]{3}-\d{4} \d{2}:\d{2}:\d{2} UTC\]/', $line, $matches)) { + if (!empty($currentError)) { + $currentError['text'] = $this->minimize_error_text($currentError['text']); + $errors[] = $currentError; + $currentError = array(); + } + + $currentError = $this->get_error_from_line($line); + + } else { + /* Is is the next line of an existing error */ + $currentError['text'] .= "\n" . $line; + } + } + + if (!empty($currentError)) { + $currentError['text'] = $this->minimize_error_text($currentError['text']); + $errors[] = $currentError; + } + + return $errors; + } + + function get_error_from_line($line) { + /* Extract values */ + $date_parts = explode(']', $line); + $date = str_replace('[', '', $date_parts[0]); + $text = trim(substr($line, strlen('[' . $date . ']'), -1)); + + /* Extract type */ + $type = 'none'; + $text_parts_type = explode(':', $text); + switch ($text_parts_type[0]) { + case 'PHP Warning': + $type = 'php-warning'; + break; + case 'PHP Parse error': + $type = 'php-parse'; + break; + case 'PHP Deprecated': + $type = 'php-deprecated'; + break; + case 'PHP Fatal error': + $type = 'php-fatal'; + break; + default: + } + + /* Return value */ + return array( + 'date' => $date, + 'type' => $type, + 'text' => $text + ); + } + + /* ---------------------------------------------------------- + Helpers + ---------------------------------------------------------- */ + + function minimize_get_correspondances() { + return array( + 'abs' => ABSPATH, + 'plug' => 'wp-content/plugins/' + ); + } + + function expand_error_text($text) { + $correspondances = $this->minimize_get_correspondances(); + foreach ($correspondances as $min => $max) { + $text = str_replace('#!' . $min . '!#', $max, $text); + } + return $text; + } + + function minimize_error_text($text) { + $correspondances = $this->minimize_get_correspondances(); + foreach ($correspondances as $min => $max) { + $text = str_replace($max, '#!' . $min . '!#', $text); + } + return $text; + } + + function array_to_html_table($array, $args = array()) { + $default_args = array( + 'table_classname' => 'widefat', + 'colnames' => array() + ); + if (!is_array($args)) { + $args = array(); + } + $args = array_merge($default_args, $args); + + $html = ''; + + // HEAD + $html .= ''; + foreach ($array[0] as $key => $value) { + $label = htmlspecialchars($key); + if (isset($args['colnames'][$key])) { + $label = $args['colnames'][$key]; + } + $html .= ''; + } + $html .= ''; + + $html .= ''; + foreach ($array as $key => $value) { + $html .= ''; + foreach ($value as $key2 => $value2) { + $html .= ''; + } + $html .= ''; + } + $html .= ''; + $html .= '
' . $label . '
' . htmlspecialchars($value2) . '
'; + return $html; + } + +} + +$WPUErrorLogs = new WPUErrorLogs();