Pinora. * * Invitation links, key, Slack webhook and the optional credit on the board. * * @package Pinora */ defined( 'ABSPATH' ) || exit; /** Admin page slug. */ const PINORA_PAGE = 'pinora'; /** * Adds the page under Tools. */ function pinora_admin_menu() { add_management_page( __( 'Pinora', 'pinora' ), __( 'Pinora', 'pinora' ), pinora_capability(), PINORA_PAGE, 'pinora_admin_page' ); } add_action( 'admin_menu', 'pinora_admin_menu' ); /** * URL of the settings page, with optional query arguments. * * @param array $args Query arguments. * @return string */ function pinora_admin_url( $args = array() ) { return add_query_arg( $args, admin_url( 'tools.php?page=' . PINORA_PAGE ) ); } /** * "Settings" link in the plugin list. * * @param array $links Action links. * @return array */ function pinora_action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'pinora' ) . '' ); return $links; } add_filter( 'plugin_action_links_' . plugin_basename( PINORA_FILE ), 'pinora_action_links' ); /** * Extra links under the plugin description. * * @param array $links Row meta links. * @param string $file Plugin file. * @return array */ function pinora_row_meta( $links, $file ) { if ( plugin_basename( PINORA_FILE ) !== $file ) { return $links; } $links[] = '' . esc_html__( 'Source code', 'pinora' ) . ''; $links[] = '' . esc_html__( 'Need a hand with your site?', 'pinora' ) . ''; return $links; } add_filter( 'plugin_row_meta', 'pinora_row_meta', 10, 2 ); /** * Stops a form sent to `admin-post.php` by someone without the capability. * Each handler then checks its own nonce. */ function pinora_admin_check_capability() { if ( ! current_user_can( pinora_capability() ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to do that.', 'pinora' ), 403 ); } } /** * Generates a new key on request. */ function pinora_admin_regenerate() { pinora_admin_check_capability(); check_admin_referer( 'pinora_regenerate' ); update_option( 'pinora_key', wp_generate_password( 20, false, false ), false ); wp_safe_redirect( pinora_admin_url( array( 'pinora_msg' => 'regenerated' ) ) ); exit; } add_action( 'admin_post_pinora_regenerate', 'pinora_admin_regenerate' ); /** * Saves the Slack webhook, and sends a test message when asked. */ function pinora_admin_slack() { pinora_admin_check_capability(); check_admin_referer( 'pinora_slack' ); $webhook = isset( $_POST['webhook'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['webhook'] ) ) ) : ''; $args = array( 'pinora_msg' => 'slack_saved' ); if ( '' !== $webhook && ! pinora_slack_is_valid( $webhook ) ) { $args['pinora_msg'] = 'slack_invalid'; } else { update_option( 'pinora_slack_webhook', $webhook, false ); if ( '' !== $webhook && isset( $_POST['test'] ) ) { $test = pinora_slack_send( array( 'id' => 0, 'description' => __( 'Test message: new feedback notes will show up here.', 'pinora' ), 'author' => wp_get_current_user()->display_name, 'url' => (string) wp_parse_url( home_url( '/' ), PHP_URL_PATH ), 'severity' => 'nice', 'sectionLabel' => __( 'Test', 'pinora' ), ) ); if ( is_wp_error( $test ) ) { $args = array( 'pinora_msg' => 'slack_failed', 'pinora_detail' => rawurlencode( $test->get_error_message() ), ); } else { $args['pinora_msg'] = 'slack_tested'; } } } wp_safe_redirect( pinora_admin_url( $args ) ); exit; } add_action( 'admin_post_pinora_slack', 'pinora_admin_slack' ); /** * Saves the board credit preference. */ function pinora_admin_credit() { pinora_admin_check_capability(); check_admin_referer( 'pinora_credit' ); update_option( 'pinora_credit', empty( $_POST['credit'] ) ? 0 : 1, false ); wp_safe_redirect( pinora_admin_url( array( 'pinora_msg' => 'saved' ) ) ); exit; } add_action( 'admin_post_pinora_credit', 'pinora_admin_credit' ); /** * Styles and the copy buttons, on the settings page only. * * @param string $hook Current admin page. */ function pinora_admin_assets( $hook ) { if ( 'tools_page_' . PINORA_PAGE !== $hook ) { return; } $css = <<<'CSS' .pinora-admin { max-width: 1180px; } .pinora-admin-hero { display: flex; align-items: center; gap: 14px; margin: 18px 0 6px; } .pinora-admin-logo { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: #714edd; color: #fff; } .pinora-admin-logo .dashicons { width: 26px; height: 26px; font-size: 26px; } .pinora-admin-hero h1 { margin: 0; padding: 0; font-size: 26px; } .pinora-admin-hero p { margin: 2px 0 0; color: #50575e; } .pinora-admin-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 20px; align-items: start; margin-top: 16px; } @media (max-width: 1000px) { .pinora-admin-grid { grid-template-columns: 1fr; } } .pinora-admin .card { max-width: none; margin: 0 0 20px; padding: 18px 22px; } .pinora-admin .card h2 { margin: 0 0 6px; font-size: 16px; } .pinora-admin .card > p:first-of-type { margin-top: 0; } .pinora-copy { display: flex; gap: 8px; margin: 6px 0 4px; } .pinora-copy input { flex: 1; min-width: 0; font-family: Menlo, Consolas, monospace; font-size: 12px; } .pinora-steps { margin: 8px 0 0 18px; } .pinora-steps li { margin-bottom: 4px; } .pinora-count { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px; background: #eae2fe; color: #5b3cc4; font-size: 12px; font-weight: 600; vertical-align: middle; } .pinora-studio { background: #1e0e4e !important; color: #fff; border: 0 !important; } .pinora-studio h2, .pinora-studio p { color: #fff; } .pinora-studio p { opacity: .85; } .pinora-studio .button-primary { background: #feffb7; border-color: #feffb7; color: #1e0e4e; } .pinora-studio .button-primary:hover, .pinora-studio .button-primary:focus { background: #fff; border-color: #fff; color: #1e0e4e; } .pinora-studio a:not(.button) { color: #feffb7; } .pinora-studio-logo { margin: 0 0 14px; } .pinora-studio-logo img { display: block; width: 140px; height: auto; opacity: 1; } .pinora-studio .pinora-studio-logo { opacity: 1; } CSS; $js = <<<'JS' document.addEventListener('click', function (e) { var ask = e.target.closest('[data-pinora-confirm]'); if (ask && !window.confirm(ask.getAttribute('data-pinora-confirm'))) { e.preventDefault(); return; } if (e.target.matches('.pinora-copy input')) e.target.select(); var b = e.target.closest('[data-pinora-copy]'); if (!b) return; var field = document.getElementById(b.getAttribute('data-pinora-copy')); var done = function () { var label = b.textContent; b.textContent = b.getAttribute('data-done'); setTimeout(function () { b.textContent = label; }, 1500); }; if (navigator.clipboard) navigator.clipboard.writeText(field.value).then(done, function () { field.select(); }); else { field.select(); document.execCommand('copy'); done(); } }); JS; wp_register_style( 'pinora-admin', false, array(), PINORA_VERSION ); wp_enqueue_style( 'pinora-admin' ); wp_add_inline_style( 'pinora-admin', $css ); wp_register_script( 'pinora-admin', false, array(), PINORA_VERSION, array( 'in_footer' => true ) ); wp_enqueue_script( 'pinora-admin' ); wp_add_inline_script( 'pinora-admin', $js ); } add_action( 'admin_enqueue_scripts', 'pinora_admin_assets' ); /** * Read-only field with a copy button. * * @param string $id Field ID. * @param string $value Value. * @param string $label Accessible name of the field. */ function pinora_copy_field( $id, $value, $label ) { ?>
array( 'warning', __( 'New key generated. Links sent before no longer work: send the new link below.', 'pinora' ) ), 'slack_saved' => array( 'success', __( 'Slack setting saved.', 'pinora' ) ), 'slack_tested' => array( 'success', __( 'Test message sent: check your Slack channel.', 'pinora' ) ), 'slack_invalid' => array( 'error', __( 'This is not a Slack webhook URL (it must start with https://hooks.slack.com/).', 'pinora' ) ), /* translators: %s: error returned by Slack. */ 'slack_failed' => array( 'error', sprintf( __( 'Slack refused the message: %s', 'pinora' ), $detail ) ), 'saved' => array( 'success', __( 'Settings saved.', 'pinora' ) ), ); if ( isset( $messages[ $code ] ) ) { printf( '

%2$s

', esc_attr( $messages[ $code ][0] ), esc_html( $messages[ $code ][1] ) ); } } /** * Renders the settings page. */ function pinora_admin_page() { $count = pinora_count(); ?>

PINORA_KEY', 'wp-config.php' ); ?>

__( 'Links already sent will stop working. Continue?', 'pinora' ) ) ); ?>

PINORA_SLACK_WEBHOOK', 'wp-config.php' ); ?>