[uS$z)uーqܐw3_xyĈc%U-%q.>^cC}! K̊Hp>"XM-j]Ux!csd2,iJmSi͐\ ,BIM|s`ZNf[$L{14ͤ8?f"E_ mhs9ܰ|| uyRVY5-(*a'`7)F?c(klLI3רӌ_zPE>'; ŁB7+v$̆(3> I'*F͖%vWfmMqi D r8M&$(J^o?x5Eƀ,i! LЇC`bH d=C,A+{6D*3[k8: jцj*fC53Lޅ%x1/ӟEgm0rI&Y\_1\}"WC/2I97ENwZ%%=@tVƪҜ"]$CYN #1&svf}do%@cس2']RʍC_]/._#59o?`9>L3B-AYYkH"֯ j<ҕlNoҸ{"[2&UQUш;a1jʮW&d 9b n-@ԃb~@g/+^}4k?qд,]쨬5M(Qtr.?mׇ/sNs=Ě/wi*fӓ,<5{rrJfkaRB"2R%Cks.%&( H|$3].0A;+k\vڍ BB?(LsS=yw2$,U#5u1kH`;d|IEf( S BE ~I|U'rOq, \#Bk[H1t`Yn+HCU>:[Smû(ll n%LN`XZc4? ]ɧ)>prn…}Ru=ɋtHK8<)bHR!1;adλ)C7KsCis@>yذkS20*:Fͯ vGk_ͯР&2Ҩ$@fg '* !G'K{rh3g_Ob%GtU[ pyBqK0$,2R|bZ!kC^O*$ؠ M*LEe,JpzݭChv{5üH2(V, ` %UY4 BZ3#s8󺓁af\|[PlR5Nu=D'[Rd.`Wfe"aRS\м)*McRaͧpEo '[8VA".lIfp,$*\בF#K+YjjuxTg>MC*BoV|^"&Ҧ]J(KI'q]rgshфU;I8̪[SOS↣{5˛4ehNΪbY@t*dWl?BXE %R%L<6ْԂA9njBW26]U0~jgu4‡{QP+Uʧ?B #WM.dW&@Q S|O,Hiӥ9xHSuX qK-<lYg!Xͱ |ߝ!24#L B+?I^[5&]%z~^2,ߡsw66uQ0wRxrIER ˉűIWRpj7.~4}RRC6YnlQEv*iqK)dy U2CcƧ4§X&a"/덩CwL٠셪qH=E #ԹzZVkaeԕp;dx_vu^Dl1; ͧZd_l@80q{fA+-J7_y^>$Trqmj߹=Wb. {)\`Hqc׆cs>xr]a2ru-!K:J`I6 M9w!s+6ƧW9‹ B0; <[Cޕo %cr }BT}J)Y"7붇h<{֐=c/./,۷zA/cJ䧺!Э#'q;= S~}:\ɼ3y!o돇ӒoO). _͝^_(Zml[GWu7##-pig~{!݅uJ|n"1\vefcCq;^rϥ/i_i]oXV^gAWa\~bJ @uLR~8!o3߇r`_);2A -v / řE~pk$(f>MНWK/ǯR=XM>4+r_j!,5aZ$9%<2oyg%X<W )2H]JA% ~[e,j#Ăz* y f-\"B˔ !#h wI˟} 0/ /Ot):ɢ4*! h2Գ\\,yP=BF:O`nG!y<:za6)V7d5;:By\UO?Zpod`0"jΣTPBI{y]H 7aeHLmK1=![׽ 03C0 cge"7k+c{Zo^P 0D\[]b([pFj$_"%C7tҋpH`\T=602W#|:.'_ ` ߹TB Fjk F. U)Z\T%pP5Ey8SÛŌ1'X捊 Vwtq(lD助`R*p{9ű@4P.5xsc 'FиXH^' ,0a-X`La*]mNsJ3#& Fu)rz.q2(oZXZPU $?Y@bȉt-x;=Lu7( HaF}ězDKalMN#65Ky9~}'_ƹ^rYUß{|T/jq8IcX .=P7a xWk^p(^ Nݜ*a$7[XCƒ0X8J0=wb\R'>ZUuuvdiLJim4zкM$bG@J8x?e=V&RKj0K9p{HHQXYY `h# s -!ۈ+&B2Mp~spcԫwyS˗a#:HtwA.L 4#T Ǯu0&}g`f7:e?PJ#$wBJ:J'.桕 s`(='v(}1ԜdV25"H@+:EfmPඩH_R4Vb-gL~9eI-/Lmӕ=e}aj::@V:~8lJݝIO%\{>gpkm*x{DDycJ}x= u\/þ t(Ԛvu褱'~6BDv#e/oOR'.!8G7\=eR2 Lqb?]!`mU $w@b>YkWNô!OrtQUVN⠦Y940N@׆?Gլy8F"m&8Ij}|+0x'y&E3Ƈrcy߆4.^ &[WGM[d "e+@jYBb]JFyl{ _(?<,c7jlsˎm:c$ 33Q>2ߣ3YE.<ָݖ pWdW&m-J&V7m!jZ.9jw<ɗzgV?ѹUydM؄oeC4T<ן5#a+ 9lrBJ8=;g9bkٚ]'*y;ܾnNS\VmTR DL$XiYlO-؟wZ.d`֖)s>JV}O#{q AibLbmй7;zvQ%J{ӍL=sS !sRYٌ DUdY>84gKV$FjZ KLuH6rX\.b~|sG+ZߚQ F2K1hZ\Y~Ի-o}r{T="Z_ P,cHxX p)*v owIĹ"jǮݼ-nѨaT>a{:j%rʼZ̪-r0"8;UGRQ@G} vpˢfRpV-2|k譾sconfig[$opt]['label'] ) ? $this->config[$opt]['label'] : ''; } /** * Resets plugin options * * @return array */ function reset_options() { $options = $this->get_options( false ); return $options; } /** * Sanitize user inputs prior to saving */ function sanitize_inputs( $inputs ) { do_action( $this->get_hook( 'before_save_options' ), $this ); if ( isset( $_POST['Reset'] ) ) { $options = $this->reset_options(); add_settings_error( 'general', 'settings_reset', __( 'Settings reset.', $this->textdomain ), 'updated' ); } else { // Start with the existing options, then start overwriting their potential override value. (This prevents // unscrupulous addition of fields by the user) $options = $this->get_options(); $option_names = $this->get_option_names(); foreach ( $option_names as $opt ) { if ( !isset( $inputs[$opt] ) ) { if ( $this->config[$opt]['input'] == 'checkbox' ) $options[$opt] = ''; elseif ( ( $this->config[$opt]['required'] === true ) ) { $msg = sprintf( __( 'A value is required for: "%s"', $this->textdomain ), $this->config[$opt]['label'] ); add_settings_error( 'general', 'setting_required', $msg, 'error' ); } } else { $val = $inputs[$opt]; $error = false; if ( empty( $val ) && ( $this->config[$opt]['required'] === true ) ) { $msg = sprintf( __( 'A value is required for: "%s"', $this->textdomain ), $this->config[$opt]['label'] ); $error = true; } else { $input = $this->config[$opt]['input']; switch ( $this->config[$opt]['datatype'] ) { case 'checkbox': break; case 'int': if ( !empty( $val ) && ( !is_numeric( $val ) || ( intval( $val ) != round( $val ) ) ) ) { $msg = sprintf( __( 'Expected integer value for: %s', $this->textdomain ), $this->config[$opt]['label'] ); $error = true; $val = ''; } break; case 'array': if ( empty( $val ) ) $val = array(); elseif ( $input == 'text' ) $val = explode( ',', str_replace( array( ', ', ' ', ',' ), ',', $val ) ); else $val = array_map( 'trim', explode( "\n", trim( $val ) ) ); break; case 'hash': if ( !empty( $val ) && $input != 'select' ) { $new_values = array(); foreach ( explode( "\n", $val ) AS $line ) { list( $shortcut, $text ) = array_map( 'trim', explode( "=>", $line, 2 ) ); if ( !empty( $shortcut ) ) $new_values[str_replace( '\\', '', $shortcut )] = str_replace( '\\', '', $text ); } $val = $new_values; } break; } } if ( $error ) add_settings_error( 'general', 'setting_not_int', $msg, 'error' ); $options[$opt] = $val; } } $options = apply_filters( $this->get_hook( 'before_update_option' ), $options, $this ); } return $options; } /** * Initializes the plugin's configuration and localizable text variables. * * @return void */ function load_config() { die( 'Function load_config() must be overridden in sub-class.' ); } /** * Verify that the necessary configuration files were set in the inheriting class. * * @return void */ function verify_config() { // Ensure required configuration options have been configured via the sub-class. Die if any aren't. foreach ( $this->required_config as $config ) { if ( empty( $this->$config ) ) die( "The plugin configuration option '$config' must be supplied." ); } // Set/change configuration options based on sub-class changes. if ( empty( $this->config ) ) $this->show_admin = false; else { // Initialize any option attributes that weren't specified by the plugin foreach ( $this->get_option_names( true ) as $opt ) { foreach ( array( 'datatype', 'default', 'help', 'input', 'input_attributes', 'label', 'no_wrap', 'options', 'output' ) as $attrib ) { if ( !isset( $this->config[$opt][$attrib] ) ) $this->config[$opt][$attrib] = ''; } $this->config[$opt]['allow_html'] = false; $this->config[$opt]['class'] = array(); } } } /** * Loads the localization textdomain for the plugin. * * @return void */ function load_textdomain() { $subdir = empty( $this->textdomain_subdir ) ? '' : '/'.$this->textdomain_subdir; load_plugin_textdomain( $this->textdomain, false, basename( dirname( $this->plugin_file ) ) . $subdir ); } /** * Registers filters. * NOTE: This occurs during the 'init' filter, so you can't use this to hook anything that happens earlier * * @return void */ function register_filters() { // This should be overridden in order to define filters. } /** * Outputs simple contextual help text, comprising solely of a thickboxed link to the plugin's hosted readme.txt file. * * NOTE: If overriding this in a sub-class, before sure to include the * check at the beginning of the function to ensure it shows up on its * own settings admin page. * * @param string $contextual_help The default contextual help * @param int $screen_id The screen ID * @param object $screen The screen object (only supplied in WP 3.0) * @return void (Text is echoed) */ function contextual_help( $contextual_help, $screen_id, $screen = null ) { if ( $screen_id != $this->options_page ) return $contextual_help; $help_url = admin_url( "plugin-install.php?tab=plugin-information&plugin={$this->id_base}&TB_iframe=true&width=640&height=656" ); echo '

'; echo '' . __( 'Click for more help on this plugin', $this->textdomain ) . '' . __( ' (especially check out the "Other Notes" tab, if present)', $this->textdomain ); echo ".

\n"; return; } /** * Outputs CSS into admin head of the plugin's settings page * * @return void */ function add_c2c_admin_css() { global $c2c_plugin_max_css_version, $c2c_plugin_css_was_output; if ( ( $c2c_plugin_max_css_version != $this->plugin_css_version ) || ( isset( $c2c_plugin_css_was_output ) && $c2c_plugin_css_was_output ) ) return; $c2c_plugin_css_was_output = true; $logo = plugins_url( 'c2c_minilogo.png', $this->plugin_file ); /** * Remember to increment the plugin_css_version variable if changing the CSS */ echo << .long-text {width:95% !important;} #c2c { text-align:center; color:#888; background-color:#ffffef; padding:5px 0 0; margin-top:12px; border-style:solid; border-color:#dadada; border-width:1px 0; } #c2c div { margin:0 auto; padding:5px 40px 0 0; width:45%; min-height:40px; background:url('$logo') no-repeat top right; } #c2c span { display:block; font-size:x-small; } .form-table {margin-bottom:20px;} .c2c-plugin-list {margin-left:2em;} .c2c-plugin-list li {list-style:disc outside;} .wrap {margin-bottom:30px !important;} .c2c-form input[type="checkbox"] {width:1.5em;} .c2c-form .hr, .c2c-hr {border-bottom:1px solid #ccc;padding:0 2px;margin-bottom:6px;} .c2c-input-help {color:#777;font-size:x-small;} .c2c-fieldset {border:1px solid #ccc; padding:2px 8px;} .c2c-textarea, .c2c-inline_textarea {width:95%;font-family:"Courier New", Courier, mono;} .c2c-nowrap { white-space:nowrap;overflow:scroll;overflow-y:hidden;overflow-x:scroll;overflow:-moz-scrollbars-horizontal } .see-help {font-size:x-small;font-style:italic;} .more-help {display:block;margin-top:8px;} CSS; } /** * Registers the admin options page and the Settings link. * * @return void */ function admin_menu() { add_filter( 'plugin_action_links_' . $this->plugin_basename, array( &$this, 'plugin_action_links' ) ); switch ( $this->settings_page ) { case 'options-general' : $func_root = 'options'; break; case 'themes' : $func_root = 'theme'; break; default : $func_root = $this->settings_page; } $menu_func = 'add_' . $func_root . '_page'; if ( function_exists( $menu_func ) ) $this->options_page = call_user_func( $menu_func, $this->name, $this->menu_name, 'manage_options', $this->plugin_basename, array( &$this, 'options_page' ) ); } /** * Adds a 'Settings' link to the plugin action links. * * @param int $limit The default limit value for the current posts query. * @return array Links associated with a plugin on the admin Plugins page */ function plugin_action_links( $action_links ) { $settings_link = '' . __( 'Settings', $this->textdomain ) . ''; array_unshift( $action_links, $settings_link ); return $action_links; } /** * See if the setting is pertinent to this version of WP * * @since 013 * * @param string $opt The option name * @return bool If the option is valid for this version of WP */ function is_option_valid( $opt ) { global $wp_version; $valid = true; $ver_operators = array( 'wpgt' => '>', 'wpgte' => '>=', 'wplt' => '<', 'wplte' => '<=' ); foreach ( $ver_operators as $ver_check => $ver_op ) { if ( isset( $this->config[$opt][$ver_check] ) && !empty( $this->config[$opt][$ver_check] ) && !version_compare( $wp_version, $this->config[$opt][$ver_check], $ver_op ) ) { $valid = false; break; } } return $valid; } /** * Returns the list of option names. * * @param bool $include_non_options (optional) Should non-options be included? Default is false. * @return array Array of option names. */ function get_option_names( $include_non_options = false ) { if ( !$include_non_options && !empty( $this->option_names ) ) return $this->option_names; if ( $include_non_options ) return array_keys( $this->config ); $this->option_names = array(); foreach ( array_keys( $this->config ) as $opt ) { if ( isset( $this->config[$opt]['input'] ) && $this->config[$opt]['input'] != '' && $this->config[$opt]['input'] != 'none' && $this->is_option_valid( $opt ) ) $this->option_names[] = $opt; } return $this->option_names; } /** * Returns either the buffered array of all options for the plugin, or * obtains the options and buffers the value. * * @param bool $with_current_values (optional) Should the currently saved values be returned? If false, then the plugin's defaults are returned. Default is true. * @return array The options array for the plugin (which is also stored in $this->options if !$with_options). */ function get_options( $with_current_values = true ) { if ( $with_current_values && !empty( $this->options ) ) return $this->options; // Derive options from the config $options = array(); $option_names = $this->get_option_names( !$with_current_values ); foreach ( $option_names as $opt ) $options[$opt] = $this->config[$opt]['default']; if ( !$with_current_values ) return $options; $this->options = wp_parse_args( get_option( $this->admin_options_name ), $options ); // Un-escape fields foreach ( $option_names as $opt ) { if ( $this->config[$opt]['allow_html'] == true ) { if ( is_array( $this->options[$opt] ) ) { foreach ( $this->options[$opt] as $key => $val ) { $new_key = wp_specialchars_decode( $key, ENT_QUOTES ); $new_val = wp_specialchars_decode( $val, ENT_QUOTES ); $this->options[$opt][$new_key] = $new_val; if ( $key != $new_key ) unset( $this->options[$opt][$key] ); } } else { $this->options[$opt] = wp_specialchars_decode( $this->options[$opt], ENT_QUOTES ); } } } return apply_filters( $this->get_hook( 'options' ), $this->options ); } /** * Gets the name to use for a form's * * @param string $prefix A prefix string, unique to the form * @return string The name */ function get_form_submit_name( $prefix ) { return $prefix . '_' . $this->u_id_base; } /** * Returns the URL for a plugin's form to use for its action attribute * * @return string The action URL */ function form_action_url() { return $_SERVER['PHP_SELF'] . '?page=' . $this->plugin_basename; } /** * Checks if the plugin's settings page has been submitted. * * @param string $prefix The prefix for the form's unique submit hidden input field * @return bool True if the plugin's settings have been submitted for saving, else false. */ function is_submitting_form( $prefix ) { return ( isset( $_POST['option_page'] ) && ( $_POST['option_page'] == $this->admin_options_name ) ); } /** * Checks if the current page is the plugin's settings page. * * @return bool True if on the plugin's settings page, else false. */ function is_plugin_admin_page() { global $pagenow; return ( basename( $pagenow, '.php' ) == $this->settings_page && isset( $_REQUEST['page'] ) && $_REQUEST['page'] == $this->plugin_basename ); } /** * Outputs the markup for an option's form field (and surrounding markup) * * @param string $opt The name/key of the option. * @return void */ function display_option( $opt ) { do_action( $this->get_hook( 'pre_display_option' ), $opt ); $options = $this->get_options(); foreach ( array( 'datatype', 'input' ) as $attrib ) $$attrib = isset( $this->config[$opt][$attrib] ) ? $this->config[$opt][$attrib] : ''; if ( $input == '' || $input == 'none' ) return; elseif ( $input == 'custom' ) { do_action( $this->get_hook( 'custom_display_option' ), $opt ); return; } $value = isset( $options[$opt] ) ? $options[$opt] : ''; $popt = $this->admin_options_name . "[$opt]"; if ( $input == 'multiselect' ) { // Do nothing since it needs the values as an array $popt .= '[]'; } elseif ( $datatype == 'array' ) { if ( !is_array( $value ) ) $value = ''; else { if ( $input == 'textarea' || $input == 'inline_textarea' ) $value = implode( "\n", $value ); else $value = implode( ', ', $value ); } } elseif ( $datatype == 'hash' && $input != 'select' ) { if ( !is_array( $value ) ) $value = ''; else { $new_value = ''; foreach ( $value AS $shortcut => $replacement ) $new_value .= "$shortcut => $replacement\n"; $value = $new_value; } } $attributes = $this->config[$opt]['input_attributes']; $this->config[$opt]['class'][] = 'c2c-' . $input; if ( ( 'textarea' == $input || 'inline_textarea' == $input ) && $this->config[$opt]['no_wrap'] ) { $this->config[$opt]['class'][] = 'c2c-nowrap'; $attributes .= ' wrap="off"'; // Unfortunately CSS is not enough } elseif ( in_array( $input, array( 'text', 'long_text', 'short_text' ) ) ) { $this->config[$opt]['class'][] = ( ( $input == 'short_text' ) ? 'small-text' : 'regular-text' ); if ( $input == 'long_text' ) $this->config[$opt]['class'][] = ' long-text'; } $class = implode( ' ', $this->config[$opt]['class'] ); $attribs = "name='$popt' id='$opt' class='$class' $attributes"; if ( $input == '' ) { // Change of implementation prevents this from being possible (since this function only gets called for registered settings) // if ( !empty( $this->config[$opt]['output'] ) ) // echo $this->config[$opt]['output'] . "\n"; // else // echo '
 
' . "\n"; } elseif ( $input == 'textarea' || $input == 'inline_textarea' ) { if ( $input == 'textarea' ) echo ""; echo "\n"; } elseif ( $input == 'select' ) { echo ""; } elseif ( $input == 'multiselect' ) { echo '
' . "\n"; foreach ( (array) $this->config[$opt]['options'] as $sopt ) echo "$sopt
\n"; echo '
'; } elseif ( $input == 'checkbox' ) { echo "\n"; } else { // Only 'text' and 'password' should fall through to here. echo "\n"; } if ( $help = apply_filters( $this->get_hook( 'option_help'), $this->config[$opt]['help'], $opt ) ) echo "
$help\n"; do_action( $this->get_hook( 'post_display_option' ), $opt ); } /** * Outputs the descriptive text (and h2 heading) for the options page. * * Intended to be overridden by sub-class. * * @param string $localized_heading_text (optional) Localized page heading text. * @return void */ function options_page_description( $localized_heading_text = '' ) { if ( empty( $localized_heading_text ) ) $localized_heading_text = $this->name; if ( $localized_heading_text ) echo '

' . $localized_heading_text . "

\n"; if ( !$this->disable_contextual_help ) echo '

' . __( 'See the "Help" link to the top-right of the page for more help.', $this->textdomain ) . "

\n"; } /** * Outputs the options page for the plugin, and saves user updates to the * options. * * @return void */ function options_page() { $options = $this->get_options(); if ( $this->saved_settings ) echo "

" . $this->saved_settings_msg . '

'; $logo = plugins_url( basename( $_GET['page'], '.php' ) . '/c2c_minilogo.png' ); echo "
\n"; echo "
" . esc_attr__( textdomain ) . "' />
\n"; $this->options_page_description(); do_action( $this->get_hook( 'before_settings_form' ), $this ); echo "
\n"; settings_fields( $this->admin_options_name ); do_settings_sections( $this->plugin_file ); echo '' . "\n"; echo '' . "\n"; echo '
' . "\n"; do_action( $this->get_hook( 'after_settings_form' ), $this ); echo '
' . "\n"; $c2c = '' . __( 'Scott Reilly, aka coffee2code', $this->textdomain ) . ''; echo sprintf( __( 'This plugin brought to you by %s.', $this->textdomain ), $c2c ); echo '' . __( 'Did you find this plugin useful?', $this->textdomain ) . ''; echo '
' . "\n"; } /** * Returns the full plugin-specific name for a hook. * * @param string $hook The name of a hook, to be made plugin-specific. * @return string The plugin-specific version of the hook name. */ function get_hook( $hook ) { return $this->hook_prefix . '_' . $hook; } /** * Returns the URL for the plugin's readme.txt file on wordpress.org/extend/plugins * * @since 005 * * @return string The URL */ function readme_url() { return 'http://wordpress.org/extend/plugins/' . $this->id_base . '/tags/' . $this->version . '/readme.txt'; } } // end class endif; // end if !class_exists() ?>