Changeset 2450996
- Timestamp:
- 01/06/2021 04:21:15 AM (5 years ago)
- Location:
- custom-sticky-notes
- Files:
-
- 6 added
- 5 edited
-
assets (modified) (1 prop)
-
assets/icon.svg (modified) (1 diff)
-
assets/screenshot-1.png (modified) (previous)
-
assets/screenshot-2.png (added)
-
trunk/assets (added)
-
trunk/assets/css (added)
-
trunk/assets/css/csnp.css (added)
-
trunk/assets/js (added)
-
trunk/assets/js/csnp.js (added)
-
trunk/custom-sticky-notes.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
custom-sticky-notes/assets
-
Property
svn:ignore
set to
icon.old.svg
screenshot.old.png
-
Property
svn:ignore
set to
-
custom-sticky-notes/assets/icon.svg
r1263003 r2450996 1 1 <?xml version="1.0" encoding="utf-8"?> 2 <!-- Generator: Adobe Illustrator 15.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->3 2 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 4 <svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" 5 y="0px" width="104.905px" height="107.427px" viewBox="0 0 104.905 107.427" enable-background="new 0 0 104.905 107.427" 6 xml:space="preserve"> 7 <g> 8 <g> 9 <path d="M81.102,43.769c-2.422-7.441-4.646-14.497-6.472-20.156c-1.832-5.671-3.237-9.893-4.114-11.853 10 c-0.236-0.522-0.799-0.82-1.362-0.718c-2.798,0.507-15.554,3.186-28.637,6.465c-0.871-2.879-1.846-5.726-2.928-8.245 11 c-0.247-0.576-0.887-0.881-1.489-0.71c-2.093,0.598-4.717,1.27-6.816,2.419c-0.444,0.247-0.698,0.74-0.635,1.245 12 c0.001,0.009,0.468,3.811,1.041,8.146c-5.484,1.531-10.489,3.087-14.215,4.554c-1.943,0.769-3.538,1.504-4.722,2.246 13 c-0.596,0.376-1.089,0.747-1.492,1.184c-0.388,0.43-0.747,0.979-0.757,1.724l0.036,0.333l-0.006,0.001 14 c0.242,1.372,0.537,5.764,0.833,11.545c0.896,17.427,1.906,47.971,2.851,55.876c0.079,0.659,0.667,1.143,1.334,1.094 15 c24.328-1.807,81.924-12.251,81.95-12.251L96.395,86l0.007-1.113C91.604,75.106,85.943,58.648,81.102,43.769z M35.705,11.261 16 c1.887,4.658,3.48,10.414,4.68,15.317c-2.469,0.938-4.692,2.084-6.888,2.787c-0.413-2.55-0.943-6.397-1.396-9.833 17 c-0.378-2.882-0.703-5.458-0.864-6.748C32.545,12.185,34.168,11.696,35.705,11.261z M85.68,85.868 18 c-16.947,2.952-52.703,8.958-71.108,10.465c-0.574-5.97-1.192-20.244-1.781-34.015c-0.317-7.404-0.63-14.68-0.931-20.498 19 c-0.294-5.683-0.561-9.896-0.841-11.69c0.139-0.182,0.719-0.707,1.654-1.199c3.127-1.746,9.759-3.945,17.35-6.071 20 c0.44,3.24,0.901,6.436,1.242,8.308l0.544,0.82l0.97,0.173c3.447-0.818,6.398-2.585,9.484-3.608 21 c0.613-0.205,0.97-0.845,0.818-1.477c-0.536-2.228-1.161-4.683-1.863-7.171c11.787-2.952,23.365-5.426,27.395-6.216 22 c0.814,2.063,2.087,5.875,3.639,10.689c5.298,16.415,13.959,44.67,21.19,60.119C91.857,84.782,89.172,85.26,85.68,85.868z 23 M20.504,48.231c2.663-1.714,6.716-2.861,10.986-3.728c4.272-0.879,8.757-1.504,12.422-2.358 24 c3.786-0.884,7.834-1.543,11.655-2.446c1.636-0.383,3.12-0.909,4.512-1.25c0.492-0.124,0.886-0.243,1.209-0.343 25 c0.538,0.246,1.183,0.108,1.535-0.392l0.19-0.305l0.171-0.676l-0.601-1.101l-0.747-0.212c-0.538,0.029-0.698,0.134-1.082,0.237 26 c-0.349,0.107-0.773,0.239-1.279,0.369c-1.55,0.383-3.015,0.903-4.479,1.24c-3.706,0.876-7.754,1.536-11.65,2.444 27 c-3.496,0.818-7.986,1.45-12.359,2.346c-4.377,0.903-8.647,2.046-11.83,4.067c-0.581,0.373-0.752,1.145-0.381,1.729 28 C19.148,48.432,19.92,48.603,20.504,48.231z M65.12,45.011c-0.259-0.64-0.986-0.95-1.626-0.693 29 c-10.017,4.043-30.837,8.472-43.098,13.425c-0.641,0.261-0.95,0.989-0.691,1.628c0.262,0.641,0.988,0.95,1.629,0.691 30 c11.919-4.836,32.688-9.25,43.093-13.425C65.066,46.378,65.377,45.651,65.12,45.011z M66.453,54.75 31 c-3.33,1.228-10.488,3.146-15.308,4.568c-7.912,2.346-18.118,7.219-24.543,8.398c-0.68,0.124-1.126,0.778-1.001,1.457 32 c0.127,0.679,0.781,1.125,1.46,0.998c7.009-1.32,17.148-6.213,24.792-8.457c4.788-1.416,11.907-3.314,15.464-4.621 33 c0.646-0.236,0.979-0.955,0.74-1.604C67.82,54.843,67.101,54.511,66.453,54.75z M47.262,73.793 34 c-1.575,0.646-4.102,1.709-6.694,2.603c-2.579,0.898-5.27,1.606-6.885,1.597l-1.004-0.122c-0.656-0.215-1.362,0.142-1.577,0.796 35 c-0.212,0.659,0.145,1.362,0.801,1.577H31.9c0.568,0.186,1.159,0.249,1.782,0.249c2.227-0.015,4.995-0.806,7.7-1.733 36 c2.69-0.928,5.269-2.012,6.826-2.651c0.64-0.264,0.944-0.994,0.684-1.631C48.629,73.837,47.898,73.531,47.262,73.793z"/> 37 </g> 38 </g> 3 <svg version="1.1" id="Custom_Stickey_Notes" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" 4 width="108px" height="108px" viewBox="0 0 1080 1080" enable-background="new 0 0 1080 1080" xml:space="preserve"> 5 <rect x="200.939" y="438.376" fill="#979797" width="347.469" height="25.427"/> 6 <rect x="200.939" y="530.826" fill="#979797" width="467.939" height="25.426"/> 7 <rect x="200.939" y="623.283" fill="#979797" width="347.469" height="25.429"/> 8 <rect x="200.939" y="715.746" fill="#979797" width="122.18" height="25.42"/> 9 <rect x="374.672" y="715.746" fill="#979797" width="294.207" height="25.42"/> 10 <rect x="595.869" y="623.283" fill="#979797" width="73.01" height="25.429"/> 11 <path fill="#474544" d="M817.624,42.161l-29.177,70.752l28.795,32.745l-122.929,82.003l-15.389-17.509l-69.048,25.376l6.836,7.775 12 H140.675c-48.109,0-87.287,39.153-87.287,87.286v584.923H53.4c0.035,6.966,0.648,13.778,1.808,20.418 13 c9.988,57.81,60.474,101.908,121.063,101.908c0.001,0,0.002,0,0.003,0l0,0h547.771h1.573h0.002 14 c48.122,0,87.287-39.175,87.287-87.298V466.485l2.967,3.371l32.177-67.344l-15.388-17.485l93.115-115.907l28.807,32.745 15 l72.025-21.969L817.624,42.161z M251.045,1012.415c25.236-19.408,42.756-48.374,47.078-81.432l324.588,81.432H251.045z 16 M787.486,950.541c0,33.614-26.94,61.041-60.37,61.854L273.742,898.644v16.314c0,53.716-43.694,97.435-97.426,97.457h-0.042v0.003 17 c-0.001,0-0.002,0-0.003,0c-40.671,0-75.618-25.076-90.206-60.591c-4.671-11.366-7.255-23.822-7.255-36.869V330.59 18 c0-34.124,27.764-61.864,61.864-61.864h498.39l47.057,53.533l-37.752,50.277l-0.001,0.002l-43.51-43.509l-17.979,17.979 19 l124.822,124.817l17.979-17.979l-39.842-39.839l0.001-0.001l49.803-30.88l47.844,54.437V950.541z M674.8,398.968l-11.762-11.761 20 l0.001-0.002l36.94-49.188l25.792,29.339L674.8,398.968L674.8,398.968z M812.908,428.537l-1.981,4.148L787.486,406l-29.896-34.02 21 l-58.678-66.72l-32.119-36.535l-21.251-24.171l3.406-1.251l23.742-8.726l7.671,8.726l22.352,25.422l84.772,96.43l25.422,28.91 22 l10.672,12.144L812.908,428.537z M925.258,236.966L818.876,369.338l-5.968-6.79l-25.422-28.91l-56.883-64.703l-22.514-25.607 23 l0.035-0.023l140.367-93.647l-36.105-41.065l11.46-27.798L989.671,269.41l-28.331,8.62L925.258,236.966z"/> 39 24 </svg> -
custom-sticky-notes/trunk/custom-sticky-notes.php
r1340416 r2450996 1 1 <?php 2 /** 3 * Custom Sticky Notes 4 * 5 * @package CustomStickyNotes 6 * @author Ka2 7 * @copyright 2016 Ka2 8 * @license GPL-2.0-or-later 9 * 10 * @wordpress-plugin 11 * Plugin Name: Custom Sticky Notes 12 * Plugin URI: https://ka2.org/custom-sticky-notes/ 13 * Description: This plugin will add simple sticky notes in the WordPress admin bar. 14 * Version: 1.1.0 15 * Requires at least: 3.7 16 * Requires PHP: 5.3 17 * Author: ka2 18 * Author URI: https://ka2.org/ 19 * Copyright: 2015-2020 MonauralSound 20 * License: GPL v2 or later 21 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt 22 * Text Domain: custom-sticky-notes 23 * Domain Path: /langs 24 */ 2 25 /* 3 Plugin Name: Custom Sticky Notes 4 Plugin URI: https://ka2.org/ 5 Description: This plugin will add simple sticky notes in the WordPress admin bar. 6 Version: 1.0.2 7 Author: ka2 8 Author URI: https://ka2.org/ 9 Copyright: 2015-2016 MonauralSound (email : [email protected]) 10 License: GPL2 - http://www.gnu.org/licenses/gpl.txt 11 Text Domain: custom-sticky-notes 12 Domain Path: /langs 26 Custom Sticky Notes is free software: you can redistribute it and/or modify 27 it under the terms of the GNU General Public License as published by 28 the Free Software Foundation, either version 2 of the License, or 29 any later version. 30 31 Custom Sticky Notes is distributed in the hope that it will be useful, 32 but WITHOUT ANY WARRANTY; without even the implied warranty of 33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34 GNU General Public License for more details. 35 36 You should have received a copy of the GNU General Public License 37 along with Custom Sticky Notes. If not, see http://www.gnu.org/licenses/gpl-2.0.txt. 13 38 */ 14 ?> 15 <?php 16 /* Copyright 2016 ka2 (https://ka2.org/) 17 18 This program is free software; you can redistribute it and/or modify 19 it under the terms of the GNU General Public License, version 2, as 20 published by the Free Software Foundation. 21 22 This program is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU General Public License for more details. 26 27 You should have received a copy of the GNU General Public License 28 along with this program; if not, write to the Free Software 29 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 30 */ 31 ?> 32 <?php 33 define( 'CSNP_PLUGIN_VERSION', '1.0.2' ); 34 define( 'CSNP_DB_VERSION', '1.0' ); 35 define( 'CSNP', 'custom-sticky-notes' ); // This plugin domain name 39 40 defined( 'CSNP_PLUGIN_VERSION' ) or define( 'CSNP_PLUGIN_VERSION', '1.1.0' ); 41 defined( 'CSNP' ) or define( 'CSNP', 'custom-sticky-notes' ); // This plugin domain name 42 defined( 'CSNP_DEBUG' ) or define( 'CSNP_DEBUG', false ); 36 43 37 44 if ( ! class_exists( 'CustomStickyNotes' ) ) : … … 40 47 * 41 48 * @since 1.0.0 42 *43 * @see -44 49 */ 45 50 final class CustomStickyNotes { 46 47 /** 48 * Member definitions 49 */ 50 public $plugin_ajax_action = 'save_sticky_notes'; 51 52 public $errors = false; 53 54 public $logger_cache; 55 56 57 /** 58 * Instance factory 59 * 60 * @since 1.0.0 61 */ 62 public static function instance() { 63 64 static $instance = null; 65 66 if ( null === $instance ) { 67 $instance = new self; 68 $instance->init(); 69 $instance->setup_actions(); 70 } 71 72 return $instance; 73 } 74 75 private function __construct() { /* Do nothing here */ } 76 77 78 /** 79 * Initialize plugin 80 * 81 * @since 1.0.0 82 */ 83 private function init() { 84 85 // Plugin Name 86 $this->domain_name = CSNP; 87 88 // Versions 89 $this->version = CSNP_PLUGIN_VERSION; 90 $this->db_version = CSNP_DB_VERSION; 91 92 // Languages 93 $this->plugin_lang_dir = plugin_basename( plugin_dir_path( __FILE__ ) ) . '/langs'; 94 load_plugin_textdomain( $this->domain_name ) 95 or load_plugin_textdomain( $this->domain_name, false, $this->plugin_lang_dir ); 96 97 } 98 99 100 /** 101 * Plugin activation and deactivation actions 102 * 103 * @since 1.0.0 104 */ 105 private function setup_actions() { 106 107 register_activation_hook( __FILE__, array( &$this, 'plugin_activate' ) ); 108 register_deactivation_hook( __FILE__, array( &$this, 'plugin_deactivation' ) ); 109 110 add_action( 'init', array($this, 'initialization') ); // Both admin panel and frontend. 111 if ( is_admin() ) { 112 add_action( 'admin_head', array( $this, 'add_header' ) ); 113 add_action( 'admin_footer', array( $this, 'add_footer' ) ); 114 add_action( 'wp_ajax_' . $this->plugin_ajax_action, array( &$this, 'ajax_handler' ) ); 115 } else { 116 add_action( 'wp_head', array( $this, 'add_header' ) ); 117 add_action( 'wp_footer', array( $this, 'add_footer' ) ); 118 add_action( 'wp_ajax_nopriv_' . $this->plugin_ajax_action, array( &$this, 'ajax_handler' ) ); 119 } 120 121 add_action( 'wp_before_admin_bar_render', array( &$this, 'custom_toolbar' ), 999 ); 122 123 } 124 125 126 /** 127 * Plugin first common actions 128 * 129 * @since 1.0.1 130 */ 131 public function initialization() { 132 // Currently no nothing 133 } 134 135 136 /** 137 * Fire this hook when append into <head> tag for this plugin 138 * 139 * @since 1.0.0 140 * @revision 1.0.2 141 */ 142 public function add_header() { 143 144 $_add_styles = <<<EOS 145 <style> 146 #csnp-container { min-height: 28px; height: 28px; max-height: 28px; padding-top: 4px; padding-bottom: 0; } 147 #csnp-container.active { background-color: #23282d; color: #ffba00; } 148 #csnp-container .dashicons-before span, .csnp-panel-header .dashicons-before span { position: relative!important; top: -4px!important; } 149 #csnp-panel { box-sizing: border-box; -moz-box-sizing: border-box; /* cursor: move; */ display: none; position: absolute; left: -200%; background-color: #f4f4f4; padding: 8px 12px; border: 1px solid #e5e5e5; border-radius: 4px; box-shadow: 2px 2px 4px rgba(0,0,0,.1)!important; -webkit-box-shadow: 2px 2px 4px rgba(0,0,0,.1)!important; } 150 .csnp-panel-header { position: relative!important; min-height: 28px; height: 28px; max-height: 28px; padding-top: 4px!important; padding-bottom: 0!important; color: #555!important; } 151 #cache-notes { position: relative; top: -4px; left: 12px; color: #398f14; } 152 .csnp-dismiss { position: relative!important; right: -10px!important; top: 1px!important; color: #777!important; } 153 .csnp-dismiss:hover, .csnp-dismiss:focus { color: #0091cd!important; } 154 #csnp-content-body { border-radius: 3px; padding: 8px; min-width: 330px; font-size: 13px; line-height: 18px; color: #555; } 155 .csnp-panel-footer label { color: #777; } 156 #local-only { height: 18px!important; margin-top: -2px!important; margin-right: 2px!important; border-radius: 3px; } 157 #csnp-save, #csnp-clear, #csnp-close { margin-left: 6px; padding: 0 12px; border-radius: 3px; font-size: 13px; line-height: 26px; height: 28px; } 158 </style> 159 EOS; 160 161 if ( ! is_admin() ) { 162 $_add_script = <<<EOS 163 <script type="text/javascript" src="https://www.google.com/jsapi"></script> 164 EOS; 165 } else { 166 $_add_script = ''; 167 } 168 169 echo $_add_styles, $_add_script; 170 171 } 172 173 174 /** 175 * Fire this hook when append into <body> tag (just before </body>) 176 * 177 * @since 1.0.0 178 * @revision 1.0.2 179 */ 180 public function add_footer() { 181 182 $_message = __( 'Now Cached', CSNP ); 183 184 if ( ! is_admin() ) { 185 $_script_head = <<<EOS 186 <script> 187 if ( typeof $ === "undefined" ) { 188 google.load("jquery", "1"); 51 /** 52 * Member definitions 53 */ 54 public $plugin_ajax_action = 'save_sticky_notes'; 55 56 public $errors = false; 57 58 public $logger_cache; 59 60 /** 61 * Instance factory 62 * 63 * @since 1.0.0 64 */ 65 public static function instance() { 66 static $instance = null; 67 68 if ( null === $instance ) { 69 $instance = new self; 70 $instance->init(); 71 $instance->setup_actions(); 72 } 73 74 return $instance; 75 } 76 77 private function __construct() { /* Do nothing here */ } 78 79 /** 80 * Initialize plugin 81 * 82 * @since 1.0.0 83 */ 84 private function init() { 85 // Plugin Name 86 $this->domain_name = CSNP; 87 88 // Versions 89 $this->version = CSNP_PLUGIN_VERSION; 90 91 // Plugin Directory Path and URL 92 $this->dir_path = plugin_dir_path( __FILE__ ); 93 $this->dir_url = plugin_dir_url( __FILE__ ); 94 95 // Languages 96 $this->plugin_lang_dir = plugin_basename( $this->dir_path ) . '/langs'; 97 load_plugin_textdomain( $this->domain_name ) 98 or load_plugin_textdomain( $this->domain_name, false, $this->plugin_lang_dir ); 99 } 100 101 102 /** 103 * Plugin activation and deactivation actions 104 * 105 * @since 1.0.0 106 */ 107 private function setup_actions() { 108 register_activation_hook( __FILE__, array( &$this, 'plugin_activate' ) ); 109 register_deactivation_hook( __FILE__, array( &$this, 'plugin_deactivation' ) ); 110 111 add_action( 'init', array( $this, 'initialization') ); // Both admin panel and frontend. 112 if ( is_admin() ) { 113 add_action( 'admin_footer', array( $this, 'add_footer' ) ); 114 add_action( 'admin_enqueue_scripts', array( $this, 'load_csnp_resources' ) ); 115 add_action( 'wp_ajax_' . $this->plugin_ajax_action, array( &$this, 'ajax_handler' ) ); 116 } else { 117 add_action( 'wp_footer', array( $this, 'add_footer' ) ); 118 add_action( 'wp_enqueue_scripts', array( $this, 'load_csnp_resources' ) ); 119 add_action( 'wp_ajax_nopriv_' . $this->plugin_ajax_action, array( &$this, 'ajax_handler' ) ); 120 } 121 add_action( 'wp_before_admin_bar_render', array( &$this, 'custom_toolbar' ), 999 ); 122 add_action( 'login_footer', array( $this, 'add_footer' ) ); 123 add_action( 'wp_logout', array( $this, 'csnp_logout_action' ) ); 124 } 125 126 /** 127 * Plugin first common actions 128 * 129 * @since 1.0.1 130 */ 131 public function initialization() { /* Currently no nothing */ } 132 133 /** 134 * Load resources for this plugin 135 * 136 * @since 1.1.0 137 */ 138 public function load_csnp_resources() { 139 $style_file_path = $this->dir_path . 'assets/css/csnp.css'; 140 $style_file_url = $this->dir_url . 'assets/css/csnp.css'; 141 if ( @file_exists( $style_file_path ) ) { 142 $_hash = hash( 'CRC32b', filemtime( $style_file_path ) ); 143 wp_enqueue_style( 'csnp', $style_file_url . '?h=' . $_hash, [], $this->version ); 144 } 145 $script_file_path = $this->dir_path . 'assets/js/csnp.js'; 146 $script_file_url = $this->dir_url . 'assets/js/csnp.js'; 147 if ( @file_exists( $script_file_path ) ) { 148 $_hash = hash( 'CRC32b', filemtime( $script_file_path ) ); 149 wp_enqueue_script( 'csnp', $script_file_url . '?h=' . $_hash, [], $this->version, true ); 150 } 151 } 152 153 /** 154 * Fire this hook when append into <head> tag for this plugin 155 * 156 * @since 1.0.0 -> 1.0.2 157 */ 158 public function add_header() { 159 // 160 } 161 162 /** 163 * Fire this hook when append into <body> tag (just before </body>) 164 * 165 * @since 1.0.0 -> 1.0.2 166 */ 167 public function add_footer() { 168 global $current_user; 169 get_currentuserinfo(); 170 171 if ( is_user_logged_in() ) 172 return; 173 174 if ( ! get_transient( 'csnp_clear_storage' ) ) 175 return; 176 177 $internal_js = <<<JS 178 var _ls = window.localStorage,_ss = window.sessionStorage; 179 _ls.removeItem('csnp-local-cache');_ls.removeItem('csnp-options'); 180 _ss.removeItem('csnp-local-cache');_ss.removeItem('csnp-options'); 181 JS; 182 echo "<script>{$internal_js}</script>"; 183 delete_transient( 'csnp_clear_storage' ); 184 } 185 186 /** 187 * Retrieve the URL for calling Ajax 188 * 189 * @since 1.0.0 190 * 191 * @param array $args [require] 192 * @return string $ajax_url 193 **/ 194 public function ajax_url( $args = array() ) { 195 if ( ! is_array( $args ) ) 196 return; 197 198 $base_url = esc_url_raw( admin_url( 'admin-ajax.php', is_ssl() ? 'https' : 'http' ) ); 199 200 $ajax_queries = array_merge( array( 'action' => $this->plugin_ajax_action ), $args ); 201 $base_url = esc_url_raw( add_query_arg( $ajax_queries, $base_url ) ); 202 203 return wp_nonce_url( $base_url, $this->domain_name . '_' . $this->plugin_ajax_action ); 204 } 205 206 /** 207 * Add sticky notes menu to admin bar (toolbar) 208 * 209 * @since 1.0.0 210 */ 211 public function custom_toolbar() { 212 global $wp_admin_bar, $user_ID; 213 get_currentuserinfo(); 214 215 // Variables for outputting HTML 216 $_adjust_style = ! is_admin() ? ' style="padding-bottom:10px;"' : ''; 217 $_title = __( 'Sticky Notes', CSNP ); 218 $_ajax_url = $this->ajax_url( array( 'event' => 'save_usermeta' ) ); 219 $_lock_label = __( 'Locked', CSNP ); 220 $_unlock_label = __( 'Unlocked', CSNP ); 221 $_placeholder = __( "Let's start Sticky Notes!", CSNP ); 222 $_stored_content = esc_textarea( get_user_meta( $user_ID, 'csnp_sticky_notes', true ) ); 223 $_checkbox_label = __( 'Cache to local only', CSNP ); 224 $_btn_label_save = __( 'Save', CSNP ); 225 $_btn_label_clear = __( 'Clear', CSNP ); 226 $_btn_label_close = __( 'Close', CSNP ); 227 $_setting_label = __( 'Setting', CSNP ); 228 $_setting_title = __( 'Custom Sticky Notes Settings', CSNP ); 229 $_setting_item_1 = __( 'Apply Dark Theme', CSNP ); 230 $_setting_item_2 = __( 'Use Session-Storage as Caching', CSNP ); 231 $_setting_item_3 = __( 'Enable Auto Save to Local Cache', CSNP ); 232 233 $_args = array( 234 'id' => $this->domain_name, 235 'title' => '<div id="csnp-container"><span class="dashicons-before dashicons-pressthis"><span>' . $_title . '</span></span></div>', 236 'parent' => 'top-secondary', 237 'meta' => array( 238 'html' => <<<EOD 239 <div id="csnp-panel" class="csnp-panel-container"> 240 <form method="post" action="{$_ajax_url}" id="csnp-action-form"> 241 <div class="csnp-panel-header"> 242 <label for="csnp-lock-panel"> 243 <input type="checkbox" id="csnp-lock-panel" value="1"> 244 <span id="csnp-lock-icon" class="dashicons-before dashicons-unlock"></span> 245 <span id="csnp-lock-text" data-on="{$_lock_label}" data-off="{$_unlock_label}" class="text--muted">{$_unlock_label}</span> 246 </label> 247 <span id="cache-notes"></span> 248 <a href="javascript:;" class="csnp-dismiss alignright"><span class="dashicons-before dashicons-no-alt"></span></a> 249 </div> 250 <div class="csnp-panel-body"{$_adjust_style}> 251 <textarea id="csnp-content-body" name="csnp-content" placeholder="{$_placeholder}">{$_stored_content}</textarea> 252 </div> 253 <div class="csnp-panel-footer"> 254 <label for="local-only"> 255 <input type="hidden" name="local_only" value="0"> 256 <input type="checkbox" name="local_only" id="local-only" value="1"> 257 <span>{$_checkbox_label}</span> 258 </label> 259 <div class="spacer"></div> 260 <div> 261 <button type="button" id="csnp-save" class="csnp-btn csnp-btn-primary" >{$_btn_label_save}</button> 262 <button type="button" id="csnp-clear" class="csnp-btn csnp-btn-secondary">{$_btn_label_clear}</button> 263 <button type="button" id="csnp-close" class="csnp-btn csnp-btn-secondary hidden" hidden>{$_btn_label_close}</button> 264 <a href="javascript:;" id="csnp-setting" aria-label="{$_setting_label}"><span class="dashicons-before dashicons-admin-generic"></span></a> 265 </div> 266 </div> 267 <div id="csnp-config-block"> 268 <h5><span class="dashicons-before dashicons-admin-settings"></span> {$_setting_title}</h5> 269 <div class="pl-1"> 270 <label class="tgl flat">{$_setting_item_1} 271 <input type="hidden" name="dark_theme" value="0"> 272 <input type="checkbox" id="on-dark-theme" name="dark_theme" value="1"> 273 <span class="tgl-btn"></span> 274 </label> 275 </div> 276 <div class="pl-1"> 277 <label class="tgl flat">{$_setting_item_2} 278 <input type="hidden" name="use_session_storage" value="0"> 279 <input type="checkbox" id="on-session-storage" name="use_session_storage" value="1"> 280 <span class="tgl-btn"></span> 281 </label> 282 </div> 283 <div class="pl-1"> 284 <label class="tgl flat">{$_setting_item_3} 285 <input type="hidden" name="auto_save" value="0"> 286 <input type="checkbox" id="on-auto-save" name="auto_save" value="1"> 287 <span class="tgl-btn"></span> 288 </label> 289 </div> 290 </div> 291 </form> 292 </div> 293 EOD, 294 'class' => 'menupop', 295 ), 296 ); 297 298 $wp_admin_bar->add_node( $_args ); 299 } 300 301 public function csnp_logout_action( $user_id ) { 302 set_transient( 'csnp_clear_storage', $user_id, 0 ); 303 } 304 305 /** 306 * Method of the handling of Ajax call 307 * Ajax controller calls the actual processing in accordance with the requested event value 308 * 309 * @since 1.0.0 310 */ 311 public function ajax_handler() { 312 if ( ! isset( $GLOBALS['_REQUEST']['_wpnonce'] ) ) 313 $this->ajax_error( __( 'Parameters for calling Ajax is not enough.', CSNP ) ); 314 315 if ( ! wp_verify_nonce( $GLOBALS['_REQUEST']['_wpnonce'], $this->domain_name . '_' . $this->plugin_ajax_action ) ) { 316 $this->ajax_error( __( 'Failed authentication. Invalid Ajax call.', CSNP ) ); 317 } 318 319 if ( ! isset( $GLOBALS['_REQUEST']['event'] ) ) { 320 if ( is_admin() ) { 321 $this->ajax_error( __( 'Ajax event is not specified.', CSNP ) ); 322 } else { 323 wp_die(); 324 } 325 } 326 327 $event_method = 'ajax_event_' . sanitize_text_field( $GLOBALS['_REQUEST']['event'] ); 328 329 if ( ! method_exists( $this, $event_method ) ) 330 $this->ajax_error( __( 'Method handling of an Ajax event does not exist.', CSNP ) ); 331 332 $this->$event_method( $GLOBALS['_REQUEST'] ); 333 } 334 335 /** 336 * Error Handling of Ajax 337 * 338 * @since 1.0.0 339 * 340 * @param $string $error_message [optional] 341 */ 342 public function ajax_error( $error_message = null ) { 343 if ( empty( $error_message ) ) 344 $error_message = __( 'Error of Ajax.', CSNP ); 345 346 die( $error_message ); 347 } 348 349 /** 350 * Save the sticky notes to usermeta via Ajax 351 * 352 * @since 1.0.0 353 * 354 * @param array $args [optional] Array of options for modal component 355 * @return void Output the HTML document for callback on the frontend 356 */ 357 public function ajax_event_save_usermeta( $args = array() ) { 358 global $user_ID; 359 get_currentuserinfo(); 360 361 if ( array_key_exists( 'sticky_notes', $args ) && '' !== $user_ID ) { 362 $_sticky_notes = stripslashes_deep( $args['sticky_notes'] ); 363 364 update_user_meta( $user_ID, 'csnp_sticky_notes', $_sticky_notes ); 365 wp_die( __( 'Saved Successfully!', CSNP ) ); 366 } else { 367 $this->ajax_error( __( 'Ajax call is invalid.', CSNP ) ); 368 } 369 } 370 371 /** 372 * Fire an action at the time this plugin has activated. 373 * 374 * since 1.0.0 -> 1.1.0 375 */ 376 public function plugin_activate() { 377 if ( CSNP_DEBUG ) { 378 $message = sprintf( __( 'Function called: %s; %s', CSNP ), __FUNCTION__, __( 'Custom Sticky Notes plugin has activated.', CSNP ) ); 379 $this->logger( $message ); 380 } 381 } 382 383 /** 384 * Fire an action at the time this plugin was deactivation. 385 * 386 * since 1.0.0 -> 1.1.0 387 */ 388 public function plugin_deactivation() { 389 if ( CSNP_DEBUG ) { 390 $message = sprintf( __( 'Function called: %s; %s', CSNP ), __FUNCTION__, __( 'Custom Sticky Notes plugin has been deactivation.', CSNP ) ); 391 $this->logger( $message ); 392 } 393 } 394 395 /** 396 * Logger for this plugin 397 * 398 * @since 1.0.0 399 * 400 * @param string $message 401 * @param integer $logging_type 0: php system logger, 1: mail to $distination, 3: overwriting file of $distination (default), 4: to SAPI handler 402 * @param string $distination 403 * @return boolean 404 */ 405 public function logger( $message = '', $logging_type = 3, $distination = '' ) { 406 if ( ! defined( 'CSNP' ) ) 407 return; 408 409 $options = get_option( $this->domain_name ); 410 $this->logger_cache = $message; 411 412 if ( empty( $message ) || '' === trim( $message ) ) { 413 $_ret = $this->errors->get_error_message(); 414 if ( ! is_wp_error( $this->errors ) || empty( $_ret ) ) 415 return; 416 417 /** 418 * Filter WP_Error error messages to log 419 * 420 * @since 1.0.0 421 * @hook filter 422 */ 423 $message = apply_filters( 'csnp_log_message', $this->errors->get_error_message(), $this->errors ); 424 } 425 426 if ( ! in_array( intval( $logging_type ), array( 0, 1, 3, 4 ) ) ) 427 $logging_type = 3; 428 429 $current_datetime = date( 'Y-m-d H:i:s', time() ); 430 $message = preg_replace( '/(?:\n|\r|\r\n)/', ' ', trim( $message ) ); 431 $log_message = sprintf( "[%s] %s\n", $current_datetime, $message ); 432 433 if ( 3 == intval( $logging_type ) ) { 434 $this->log_distination_path = empty( $message ) || '' === trim( $distination ) ? plugin_dir_path( __FILE__ ) . 'debug.log' : $distination; 435 /** 436 * Filter the log file path 437 * 438 * @since 1.0.0 439 * @hook filter 440 */ 441 $this->log_distination_path = apply_filters( 'csnp_log_distination_path', $this->log_distination_path ); 442 } 443 444 if ( false === error_log( $log_message, $logging_type, $this->log_distination_path ) ) { 445 $this->errors = new WP_Error(); 446 $this->errors->add( 'logging_error', __( 'Failed to logging.', CSNP ) ); 447 return false; 448 } else { 449 return true; 450 } 451 } 189 452 } 190 google.setOnLoadCallback(function() { 191 $(function(){ 192 EOS; 193 $_script_foot = <<<EOS 194 }); 195 }); 196 </script> 197 EOS; 198 199 } else { 200 $_script_head = <<<EOS 201 <script> 202 jQuery(document).ready(function($){ 203 EOS; 204 $_script_foot = <<<EOS 205 }); 206 </script> 207 EOS; 208 209 } 210 211 $_script_body = <<<EOS 212 213 var saveLocalStorage = function(){ 214 var cacheData = new Array(); 215 cacheData.push($('#csnp-content-body').val()); 216 localStorage.setItem('csnp-local-cache', JSON.stringify(cacheData)); 217 $('#cache-notes').text('{$_message}'); 218 }; 219 220 var saveUserMeta = function(){ 221 222 var jqXHR = $.ajax({ 223 async: true, 224 url: $('#csnp-action-form').attr('action'), 225 type: 'get', 226 data: { 'sticky_notes': $('#csnp-content-body').val() }, 227 dataType: 'text', 228 cache: false, 229 beforeSend: function(xhr, set) { 230 // return; 231 } 232 }); 233 234 jqXHR.done(function(data, stat, xhr) { 235 if ('' === data) { 236 $('#cache-notes').text('{$_message}'); 237 } 238 }); 239 240 jqXHR.fail(function(xhr, stat, err) { 241 $('#cache-notes').text(''); 242 }); 243 244 }; 245 246 var loadLocalStorage = function(){ 247 var restoredCache = JSON.parse(localStorage.getItem('csnp-local-cache')); 248 if (restoredCache !== null && restoredCache.length > 0) { 249 $('#csnp-content-body').val( restoredCache[0] ); 250 $('#cache-notes').text('{$_message}'); 251 } else { 252 $('#cache-notes').text(''); 253 } 254 }; 255 256 $('#csnp-container').on('click', function(){ 257 $(this).toggleClass('active'); 258 if ($(this).hasClass('active')) { 259 if ($('#local-only').prop('checked')) { 260 loadLocalStorage(); 261 } 262 $('#csnp-panel').css('display', 'block'); 263 } else { 264 $('#csnp-panel').css('display', 'none'); 265 } 266 }); 267 268 $('a.csnp-dismiss, button#csnp-close').on('click', function(){ 269 $('#csnp-container').trigger('click'); 270 }); 271 272 $('button#csnp-clear').on('click', function(){ 273 $('#csnp-content-body').val(''); 274 if ($('#local-only').prop('checked')) { 275 saveLocalStorage(); 276 } else { 277 saveUserMeta(); 278 } 279 $('#cache-notes').text(''); 280 }); 281 282 $('button#csnp-save').on('click', function(e){ 283 e.preventDefault(); 284 if ($('#local-only').prop('checked')) { 285 saveLocalStorage(); 286 } else { 287 saveUserMeta(); 288 } 289 }); 290 291 $('#local-only').on('click change', function(){ 292 localStorage.setItem('csnp-local-only', $('#local-only').prop('checked')); 293 }); 294 295 if ('true' === localStorage.getItem('csnp-local-only')) { 296 $('#local-only').prop('checked', true); 297 } 298 299 EOS; 300 301 echo $_script_head, $_script_body, $_script_foot; 302 303 } 304 305 306 /** 307 * Retrieve the URL for calling Ajax 308 * 309 * @since 1.0.0 310 * 311 * @param array $args [require] 312 * @return string $ajax_url 313 **/ 314 public function ajax_url( $args=array() ) { 315 if ( ! is_array( $args ) ) 316 return; 317 318 $base_url = esc_url_raw( admin_url( 'admin-ajax.php', is_ssl() ? 'https' : 'http' ) ); 319 320 $ajax_queries = array_merge( array( 'action' => $this->plugin_ajax_action ), $args ); 321 $base_url = esc_url_raw( add_query_arg( $ajax_queries, $base_url ) ); 322 323 return wp_nonce_url( $base_url, $this->domain_name . '_' . $this->plugin_ajax_action ); 324 325 } 326 327 328 /** 329 * Add sticky notes menu to admin bar (toolbar) 330 * 331 * @since 1.0.0 332 */ 333 public function custom_toolbar() { 334 global $wp_admin_bar, $user_ID; 335 get_currentuserinfo(); 336 337 $_title = __( 'Sticky Notes', CSNP ); 338 $_ajax_url = $this->ajax_url( array( 'event' => 'save_usermeta' ) ); 339 $_stored_content = get_user_meta( $user_ID, 'csnp_sticky_notes', true ); 340 341 $_args = array( 342 'id' => $this->domain_name, 343 'title' => '<div id="csnp-container"> 344 <span class="dashicons-before dashicons-pressthis"><span>' . $_title . '</span></span> 345 </div>', 346 'parent' => 'top-secondary', 347 'meta' => array( 348 'html' => '<div id="csnp-panel" class="wp-core-ui csnp-panel-container" style="display: none;"><form method="post" action="' . $_ajax_url . '" id="csnp-action-form"> 349 <div class="csnp-panel-header"><span class="dashicons-before dashicons-pressthis"><span>' . $_title . '</span></span> <span id="cache-notes"></span> <a href="#" class="csnp-dismiss alignright"><span class="dashicons-before dashicons-no"></span></a></div> 350 <div class="csnp-panel-body"><textarea id="csnp-content-body" name="csnp-content" cols="42" rows="5" placeholder="'. __( "Let's start Sticky Notes!", CSNP ) .'">' . $_stored_content . '</textarea></div> 351 <div class="csnp-panel-footer"><label for="local-only" class="alignleft"><input type="checkbox" name="local_only" id="local-only" value="1"> ' . __( 'Cache to local only', CSNP ) . '</label><span class="alignright"> 352 <button type="submit" id="csnp-save" class="button-primary">' . __( 'Save', CSNP ) . '</button><button type="button" id="csnp-clear" class="button-secondary">' . __( 'Clear', CSNP ) . '</button><button type="button" id="csnp-close" class="button-secondary">' . __( 'Close', CSNP ) . '</button></span></div> 353 </form></div>', 354 'class' => 'menupop', 355 ), 356 ); 357 358 $wp_admin_bar->add_node( $_args ); 359 360 } 361 362 363 /** 364 * Method of the handling of Ajax call 365 * Ajax controller calls the actual processing in accordance with the requested event value 366 * 367 * @since 1.0.0 368 */ 369 public function ajax_handler() { 370 if ( ! isset( $GLOBALS['_REQUEST']['_wpnonce'] ) ) 371 $this->ajax_error( __( 'Parameters for calling Ajax is not enough.', CSNP ) ); 372 373 if ( ! wp_verify_nonce( $GLOBALS['_REQUEST']['_wpnonce'], $this->domain_name . '_' . $this->plugin_ajax_action ) ) { 374 $this->ajax_error( __( 'Failed authentication. Invalid Ajax call.', CSNP ) ); 375 } 376 377 if ( ! isset( $GLOBALS['_REQUEST']['event'] ) ) { 378 if ( is_admin() ) { 379 $this->ajax_error( __( 'Ajax event is not specified.', CSNP ) ); 380 } else { 381 wp_die(); 382 } 383 } 384 385 $event_method = 'ajax_event_' . rtrim( $GLOBALS['_REQUEST']['event'] ); 386 387 if ( ! method_exists( $this, $event_method ) ) { 388 $this->ajax_error( __( 'Method handling of an Ajax event does not exist.', CSNP ) ); 389 } 390 391 $this->$event_method( $GLOBALS['_REQUEST'] ); 392 393 } 394 395 396 /** 397 * Error Handling of Ajax 398 * 399 * @since 1.0.0 400 * 401 * @param $string $error_message [optional] 402 */ 403 public function ajax_error( $error_message=null ) { 404 405 if ( empty( $error_message ) ) 406 $error_message = __( 'Error of Ajax.', CSNP ); 407 408 die( $error_message ); 409 410 } 411 412 413 /** 414 * Save the sticky notes to usermeta via Ajax 415 * 416 * @since 1.0.0 417 * 418 * @param array $args [optional] Array of options for modal component 419 * @return void Output the HTML document for callback on the frontend 420 */ 421 public function ajax_event_save_usermeta( $args=array() ) { 422 423 global $user_ID; 424 get_currentuserinfo(); 425 426 if ( array_key_exists( 'sticky_notes', $args ) && '' !== $user_ID ) { 427 428 $_sticky_notes = stripslashes_deep( $args['sticky_notes'] ); 429 430 update_user_meta( $user_ID, 'csnp_sticky_notes', $_sticky_notes ); 431 //$this->ajax_error( __( 'Failed to save.', CSNP ) ); 432 wp_die(); 433 434 } else { 435 436 $this->ajax_error( __( 'Ajax call is invalid.', CSNP ) ); 437 438 } 439 440 } 441 442 443 /** 444 * Fire an action at the time this plugin has activated. 445 * 446 * since 1.0.0 447 */ 448 public function plugin_activate() { 449 450 $message = sprintf( __( 'Function called: %s; %s', CSNP ), __FUNCTION__, __( 'Custom Sticky Notes plugin has activated.', CSNP ) ); 451 $this->logger( $message ); 452 453 // as you fun 454 } 455 456 /** 457 * Fire an action at the time this plugin was deactivation. 458 * 459 * since 1.0.0 460 */ 461 public function plugin_deactivation() { 462 463 $message = sprintf( __( 'Function called: %s; %s', CSNP ), __FUNCTION__, __( 'Custom Sticky Notes plugin has been deactivation.', CSNP ) ); 464 $this->logger( $message ); 465 466 // as you fun 467 } 468 469 470 /** 471 * Logger for this plugin 472 * 473 * @since 1.0.0 474 * 475 * @param string $message 476 * @param integer $logging_type 0: php system logger, 1: mail to $distination, 3: overwriting file of $distination (default), 4: to SAPI handler 477 * @param string $distination 478 * @return boolean 479 */ 480 public function logger( $message='', $logging_type=3, $distination='' ) { 481 if ( ! defined( 'CSNP' ) ) 482 return; 483 484 $options = get_option( $this->domain_name ); 485 $this->logger_cache = $message; 486 487 if ( empty( $message ) || '' === trim( $message ) ) { 488 $_ret = $this->errors->get_error_message(); 489 if ( ! is_wp_error( $this->errors ) || empty( $_ret ) ) 490 return; 491 492 $message = apply_filters( 'csnp_log_message', $this->errors->get_error_message(), $this->errors ); 493 } 494 495 if ( ! in_array( intval( $logging_type ), array( 0, 1, 3, 4 ) ) ) 496 $logging_type = 3; 497 498 $current_datetime = date( 'Y-m-d H:i:s', time() ); 499 $message = preg_replace( '/(?:\n|\r|\r\n)/', ' ', trim( $message ) ); 500 $log_message = sprintf( "[%s] %s\n", $current_datetime, $message ); 501 502 if ( 3 == intval( $logging_type ) ) { 503 $this->log_distination_path = empty( $message ) || '' === trim( $distination ) ? plugin_dir_path( __FILE__ ) . 'debug.log' : $distination; 504 $this->log_distination_path = apply_filters( 'csnp_log_distination_path', $this->log_distination_path ); 505 } 506 507 if ( false === error_log( $log_message, $logging_type, $this->log_distination_path ) ) { 508 $this->errors = new WP_Error(); 509 $this->errors->add( 'logging_error', __( 'Failed to logging.', CSNP ) ); 510 return false; 511 } else { 512 return true; 513 } 514 } 515 516 453 454 CustomStickyNotes::instance(); 455 456 endif; // end of class_exists() 457 458 // Allow custom functions file 459 if ( file_exists( __DIR__ . '/functions.php' ) ) { 460 require_once( __DIR__ . '/functions.php' ); 517 461 } 518 519 CustomStickyNotes::instance();520 521 endif; // end of class_exists() -
custom-sticky-notes/trunk/readme.txt
r1340416 r2450996 1 1 === Custom Sticky Notes === 2 Contributors: ka23 Donate link: https:// www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2YZY4HWYSWEWG&lc=en_US¤cy_code=USD&item_name=2 Contributors: Ka2 3 Donate link: https://github.com/sponsors/ka215 4 4 Tags: custom sticky notes, admin bar, sticky notes, snippets, clipboard 5 5 Requires at least: 3.7 6 Tested up to: 4.4.17 Stable tag: 1. 0.26 Tested up to: 5.6.0 7 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 License URI: http://www.gnu.org/licenses/gpl-2.0. html9 License URI: http://www.gnu.org/licenses/gpl-2.0.txt 10 10 11 11 Add simple sticky notes in the WordPress admin bar. … … 32 32 33 33 1. If the admin bar has enabled, sticky notes will pop up anytime, anywhere. 34 2. You can now choose the color scheme of sticky notes from two themes. 34 35 35 36 == Changelog == 37 38 = 1.1.0 = 39 * Changed to internal processing that does not depend on jQuery. 40 * Improved and enhanced several UI/UX. 36 41 37 42 = 1.0.2 =
Note: See TracChangeset
for help on using the changeset viewer.