This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,164 @@
<?php
class PageNavi_Options_Page extends scbAdminPage {
function setup() {
$this->textdomain = 'wp-pagenavi';
$this->args = array(
'page_title' => __( 'PageNavi Settings', $this->textdomain ),
'menu_title' => __( 'PageNavi', $this->textdomain ),
'page_slug' => 'pagenavi',
);
}
function validate( $new_data, $old_data ) {
$options = wp_parse_args($new_data, $old_data);
foreach ( array( 'style', 'num_pages', 'num_larger_page_numbers', 'larger_page_numbers_multiple' ) as $key ) {
$options[ $key ] = absint( @$options[ $key ] );
}
foreach ( array( 'use_pagenavi_css', 'always_show' ) as $key ) {
$options[ $key ] = intval( @$options[ $key ] );
}
foreach ( array( 'pages_text', 'current_text', 'page_text', 'first_text', 'last_text', 'prev_text', 'next_text', 'dotleft_text', 'dotright_text' ) as $key ) {
$options[ $key ] = wp_kses_post( @$options[ $key ] );
}
return $options;
}
function page_content() {
$rows = array(
array(
'title' => __( 'Text For Number Of Pages', $this->textdomain ),
'type' => 'text',
'name' => 'pages_text',
'extra' => 'size="50"',
'desc' => '<br />
%CURRENT_PAGE% - ' . __( 'The current page number.', $this->textdomain ) . '<br />
%TOTAL_PAGES% - ' . __( 'The total number of pages.', $this->textdomain )
),
array(
'title' => __( 'Text For Current Page', $this->textdomain ),
'type' => 'text',
'name' => 'current_text',
'desc' => '<br />
%PAGE_NUMBER% - ' . __( 'The page number.', $this->textdomain )
),
array(
'title' => __( 'Text For Page', $this->textdomain ),
'type' => 'text',
'name' => 'page_text',
'desc' => '<br />
%PAGE_NUMBER% - ' . __( 'The page number.', $this->textdomain )
),
array(
'title' => __( 'Text For First Page', $this->textdomain ),
'type' => 'text',
'name' => 'first_text',
'desc' => '<br />
%TOTAL_PAGES% - ' . __( 'The total number of pages.', $this->textdomain )
),
array(
'title' => __( 'Text For Last Page', $this->textdomain ),
'type' => 'text',
'name' => 'last_text',
'desc' => '<br />
%TOTAL_PAGES% - ' . __( 'The total number of pages.', $this->textdomain )
),
array(
'title' => __( 'Text For Previous Page', $this->textdomain ),
'type' => 'text',
'name' => 'prev_text',
),
array(
'title' => __( 'Text For Next Page', $this->textdomain ),
'type' => 'text',
'name' => 'next_text',
),
array(
'title' => __( 'Text For Previous ...', $this->textdomain ),
'type' => 'text',
'name' => 'dotleft_text',
),
array(
'title' => __( 'Text For Next ...', $this->textdomain ),
'type' => 'text',
'name' => 'dotright_text',
),
);
$out =
html( 'h3', __( 'Page Navigation Text', $this->textdomain ) )
.html( 'p', __( 'Leaving a field blank will hide that part of the navigation.', $this->textdomain ) )
.$this->table( $rows );
$rows = array(
array(
'title' => __( 'Use pagenavi-css.css', $this->textdomain ),
'type' => 'radio',
'name' => 'use_pagenavi_css',
'choices' => array( 1 => __( 'Yes', $this->textdomain ), 0 => __( 'No', $this->textdomain ) )
),
array(
'title' => __( 'Page Navigation Style', $this->textdomain ),
'type' => 'select',
'name' => 'style',
'values' => array( 1 => __( 'Normal', $this->textdomain ), 2 => __( 'Drop-down List', $this->textdomain ) ),
'text' => false
),
array(
'title' => __( 'Always Show Page Navigation', $this->textdomain ),
'type' => 'radio',
'name' => 'always_show',
'choices' => array( 1 => __( 'Yes', $this->textdomain ), 0 => __( 'No', $this->textdomain ) ),
'desc' => '<br />'.__( "Show navigation even if there's only one page.", $this->textdomain )
),
array(
'title' => __( 'Number Of Pages To Show', $this->textdomain ),
'type' => 'text',
'name' => 'num_pages',
'extra' => 'class="small-text"'
),
array(
'title' => __( 'Number Of Larger Page Numbers To Show', $this->textdomain ),
'type' => 'text',
'name' => 'num_larger_page_numbers',
'extra' => 'class="small-text"',
'desc' =>
'<br />' . __( 'Larger page numbers are in addition to the normal page numbers. They are useful when there are many pages of posts.', $this->textdomain ) .
'<br />' . __( 'For example, WP-PageNavi will display: Pages 1, 2, 3, 4, 5, 10, 20, 30, 40, 50.', $this->textdomain ) .
'<br />' . __( 'Enter 0 to disable.', $this->textdomain )
),
array(
'title' => __( 'Show Larger Page Numbers In Multiples Of', $this->textdomain ),
'type' => 'text',
'name' => 'larger_page_numbers_multiple',
'extra' => 'class="small-text"',
'desc' =>
'<br />' . __( 'For example, if mutiple is 5, it will show: 5, 10, 15, 20, 25', $this->textdomain )
)
);
$out .=
html( 'h3', __( 'Page Navigation Options', $this->textdomain ) )
.$this->table( $rows );
echo $this->form_wrap( $out );
}
}

View File

@ -0,0 +1,335 @@
<?php
/**
* Template tag: Boxed Style Paging
*
* @param array $args:
* 'before': (string)
* 'after': (string)
* 'options': (string|array) Used to overwrite options set in WP-Admin -> Settings -> PageNavi
*
* @return void|string
*/
function wp_pagenavi( $args = array() ) {
if ( !is_array( $args ) ) {
$argv = func_get_args();
$args = array();
foreach ( array( 'before', 'after', 'options' ) as $i => $key ) {
$args[ $key ] = isset( $argv[ $i ]) ? $argv[ $i ] : '';
}
}
$args = wp_parse_args( $args, array(
'before' => '',
'after' => '',
'wrapper_tag' => 'div',
'wrapper_class' => 'wp-pagenavi',
'options' => array(),
'query' => $GLOBALS['wp_query'],
'type' => 'posts',
'echo' => true
) );
extract( $args, EXTR_SKIP );
$options = wp_parse_args( $options, PageNavi_Core::$options->get() );
$instance = new PageNavi_Call( $args );
list( $posts_per_page, $paged, $total_pages ) = $instance->get_pagination_args();
if ( 1 == $total_pages && !$options['always_show'] )
return;
$pages_to_show = absint( $options['num_pages'] );
$larger_page_to_show = absint( $options['num_larger_page_numbers'] );
$larger_page_multiple = absint( $options['larger_page_numbers_multiple'] );
$pages_to_show_minus_1 = $pages_to_show - 1;
$half_page_start = floor( $pages_to_show_minus_1/2 );
$half_page_end = ceil( $pages_to_show_minus_1/2 );
$start_page = $paged - $half_page_start;
if ( $start_page <= 0 )
$start_page = 1;
$end_page = $paged + $half_page_end;
if ( ( $end_page - $start_page ) != $pages_to_show_minus_1 )
$end_page = $start_page + $pages_to_show_minus_1;
if ( $end_page > $total_pages ) {
$start_page = $total_pages - $pages_to_show_minus_1;
$end_page = $total_pages;
}
if ( $start_page < 1 )
$start_page = 1;
// Support for filters to change class names
$class_names = array(
'pages' => apply_filters( 'wp_pagenavi_class_pages', 'pages'),
'first' => apply_filters( 'wp_pagenavi_class_first', 'first' ),
'previouspostslink' => apply_filters( 'wp_pagenavi_class_previouspostslink', 'previouspostslink' ),
'extend' => apply_filters( 'wp_pagenavi_class_extend', 'extend' ),
'smaller' => apply_filters( 'wp_pagenavi_class_smaller', 'smaller' ),
'page' => apply_filters( 'wp_pagenavi_class_page', 'page' ),
'current' => apply_filters( 'wp_pagenavi_class_current', 'current'),
'larger' => apply_filters( 'wp_pagenavi_class_larger', 'larger' ),
'nextpostslink' => apply_filters( 'wp_pagenavi_class_nextpostslink', 'nextpostslink'),
'last' => apply_filters( 'wp_pagenavi_class_last', 'last'),
);
$out = '';
switch ( intval( $options['style'] ) ) {
// Normal
case 1:
// Text
if ( !empty( $options['pages_text'] ) ) {
$pages_text = str_replace(
array( "%CURRENT_PAGE%", "%TOTAL_PAGES%" ),
array( number_format_i18n( $paged ), number_format_i18n( $total_pages ) ),
__( $options['pages_text'], 'wp-pagenavi' ) );
$out .= "<span class='{$class_names['pages']}'>$pages_text</span>";
}
if ( $start_page >= 2 && $pages_to_show < $total_pages ) {
// First
$first_text = str_replace( '%TOTAL_PAGES%', number_format_i18n( $total_pages ), __( $options['first_text'], 'wp-pagenavi' ) );
$out .= $instance->get_single( 1, $first_text, array(
'class' => $class_names['first'],
'aria-label' => __('First Page'),
), '%TOTAL_PAGES%' );
}
// Previous
if ( $paged > 1 && !empty( $options['prev_text'] ) ) {
$out .= $instance->get_single( $paged - 1, $options['prev_text'], array(
'class' => $class_names['previouspostslink'],
'rel' => 'prev',
'aria-label' => __('Previous Page'),
) );
}
if ( $start_page >= 2 && $pages_to_show < $total_pages ) {
if ( !empty( $options['dotleft_text'] ) )
$out .= "<span class='{$class_names['extend']}'>{$options['dotleft_text']}</span>";
}
// Smaller pages
$larger_pages_array = array();
if ( $larger_page_multiple )
for ( $i = $larger_page_multiple; $i <= $total_pages; $i+= $larger_page_multiple )
$larger_pages_array[] = $i;
$larger_page_start = 0;
foreach ( $larger_pages_array as $larger_page ) {
if ( $larger_page < ($start_page - $half_page_start) && $larger_page_start < $larger_page_to_show ) {
$out .= $instance->get_single( $larger_page, $options['page_text'], array(
'class' => "{$class_names['smaller']} {$class_names['page']}",
'title' => sprintf( __( 'Page %s', 'wp-pagenavi' ), number_format_i18n( $larger_page ) ),
) );
$larger_page_start++;
}
}
if ( $larger_page_start )
$out .= "<span class='{$class_names['extend']}'>{$options['dotleft_text']}</span>";
// Page numbers
$timeline = 'smaller';
foreach ( range( $start_page, $end_page ) as $i ) {
if ( $i == $paged && !empty( $options['current_text'] ) ) {
$current_page_text = str_replace( '%PAGE_NUMBER%', number_format_i18n( $i ), $options['current_text'] );
$out .= "<span aria-current='page' class='{$class_names['current']}'>$current_page_text</span>";
$timeline = 'larger';
} else {
$out .= $instance->get_single( $i, $options['page_text'], array(
'class' => "{$class_names['page']} {$class_names[$timeline]}",
'title' => sprintf( __( 'Page %s', 'wp-pagenavi' ), number_format_i18n( $i ) ),
) );
}
}
// Large pages
$larger_page_end = 0;
$larger_page_out = '';
foreach ( $larger_pages_array as $larger_page ) {
if ( $larger_page > ($end_page + $half_page_end) && $larger_page_end < $larger_page_to_show ) {
$larger_page_out .= $instance->get_single( $larger_page, $options['page_text'], array(
'class' => "{$class_names['larger']} {$class_names['page']}",
'title' => sprintf( __( 'Page %s', 'wp-pagenavi' ), number_format_i18n( $larger_page ) ),
) );
$larger_page_end++;
}
}
if ( $larger_page_out ) {
$out .= "<span class='{$class_names['extend']}'>{$options['dotright_text']}</span>";
}
$out .= $larger_page_out;
if ( $end_page < $total_pages ) {
if ( !empty( $options['dotright_text'] ) )
$out .= "<span class='{$class_names['extend']}'>{$options['dotright_text']}</span>";
}
// Next
if ( $paged < $total_pages && !empty( $options['next_text'] ) ) {
$out .= $instance->get_single( $paged + 1, $options['next_text'], array(
'class' => $class_names['nextpostslink'],
'rel' => 'next',
'aria-label' => __('Next Page'),
) );
}
if ( $end_page < $total_pages ) {
// Last
$out .= $instance->get_single( $total_pages, __( $options['last_text'], 'wp-pagenavi' ), array(
'class' => $class_names['last'],
'aria-label' => __('Last Page'),
), '%TOTAL_PAGES%' );
}
break;
// Dropdown
case 2:
$out .= '<form action="" method="get">'."\n";
$out .= '<select size="1" onchange="document.location.href = this.options[this.selectedIndex].value;">'."\n";
foreach ( range( 1, $total_pages ) as $i ) {
$page_num = $i;
if ( $page_num == 1 )
$page_num = 0;
if ( $i == $paged ) {
$current_page_text = str_replace( '%PAGE_NUMBER%', number_format_i18n( $i ), $options['current_text'] );
$out .= '<option value="'.esc_url( $instance->get_url( $page_num ) ).'" selected="selected" class="'.$class_names['current'].'">'.$current_page_text."</option>\n";
} else {
$page_text = str_replace( '%PAGE_NUMBER%', number_format_i18n( $i ), $options['page_text'] );
$out .= '<option value="'.esc_url( $instance->get_url( $page_num ) ).'">'.$page_text."</option>\n";
}
}
$out .= "</select>\n";
$out .= "</form>\n";
break;
}
$out = $before . "<" . $wrapper_tag . " class='" . $wrapper_class . "' role='navigation'>\n$out\n</" . $wrapper_tag . ">" . $after;
$out = apply_filters( 'wp_pagenavi', $out, $args );
if ( !$echo )
return $out;
echo $out;
}
class PageNavi_Call {
protected $args;
function __construct( $args ) {
$this->args = $args;
}
function __get( $key ) {
return $this->args[ $key ];
}
function get_pagination_args() {
global $numpages;
$query = $this->query;
switch( $this->type ) {
case 'multipart':
// Multipart page
$posts_per_page = 1;
$paged = max( 1, absint( get_query_var( 'page' ) ) );
$total_pages = max( 1, $numpages );
break;
case 'users':
// WP_User_Query
$posts_per_page = $query->query_vars['number'];
$paged = max( 1, floor( $query->query_vars['offset'] / $posts_per_page ) + 1 );
$total_pages = max( 1, ceil( $query->total_users / $posts_per_page ) );
break;
default:
// WP_Query
$posts_per_page = intval( $query->get( 'posts_per_page' ) );
$paged = max( 1, absint( $query->get( 'paged' ) ) );
$total_pages = max( 1, absint( $query->max_num_pages ) );
break;
}
return array( $posts_per_page, $paged, $total_pages );
}
function get_single( $page, $raw_text, $attr, $format = '%PAGE_NUMBER%' ) {
if ( empty( $raw_text ) )
return '';
$text = str_replace( $format, number_format_i18n( $page ), $raw_text );
$attr['href'] = $this->get_url( $page );
return html( 'a', $attr, $text );
}
function get_url( $page ) {
return ( 'multipart' == $this->type ) ? get_multipage_link( $page ) : get_pagenum_link( $page );
}
}
# http://core.trac.wordpress.org/ticket/16973
if ( !function_exists( 'get_multipage_link' ) ) :
function get_multipage_link( $page = 1 ) {
global $post, $wp_rewrite;
if ( 1 == $page ) {
$url = get_permalink();
} else {
if ( '' == get_option('permalink_structure') || in_array( $post->post_status, array( 'draft', 'pending') ) )
$url = add_query_arg( 'page', $page, get_permalink() );
elseif ( 'page' == get_option( 'show_on_front' ) && get_option('page_on_front') == $post->ID )
$url = trailingslashit( get_permalink() ) . user_trailingslashit( $wp_rewrite->pagination_base . "/$page", 'single_paged' );
else
$url = trailingslashit( get_permalink() ) . user_trailingslashit( $page, 'single_paged' );
}
return $url;
}
endif;
// Template tag: Drop Down Menu (Deprecated)
function wp_pagenavi_dropdown() {
wp_pagenavi();
}
class PageNavi_Core {
static $options;
static function init( $options ) {
self::$options = $options;
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'stylesheets' ) );
}
static function stylesheets() {
if ( !self::$options->use_pagenavi_css )
return;
if ( @file_exists( get_stylesheet_directory() . '/pagenavi-css.css' ) )
$css_file = get_stylesheet_directory_uri() . '/pagenavi-css.css';
elseif ( @file_exists( get_template_directory() . '/pagenavi-css.css' ) )
$css_file = get_template_directory_uri() . '/pagenavi-css.css';
else
$css_file = plugins_url( 'pagenavi-css.css', __FILE__ );
wp_enqueue_style( 'wp-pagenavi', $css_file, false, '2.70' );
}
}

View File

@ -0,0 +1,24 @@
/*
Default style for WP-PageNavi plugin
http://wordpress.org/extend/plugins/wp-pagenavi/
*/
.wp-pagenavi {
clear: both;
}
.wp-pagenavi a, .wp-pagenavi span {
text-decoration: none;
border: 1px solid #BFBFBF;
padding: 3px 5px;
margin: 2px;
}
.wp-pagenavi a:hover, .wp-pagenavi span.current {
border-color: #000;
}
.wp-pagenavi span.current {
font-weight: bold;
}

View File

@ -0,0 +1,225 @@
# WP-PageNavi
Contributors: GamerZ, scribu
Donate link: https://lesterchan.net/site/donation/
Tags: navigation, pagination, paging, pages
Requires at least: 3.2
Tested up to: 6.3
Stable tag: 2.94.1
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Adds a more advanced paging navigation interface.
## Description
Want to replace the old *&larr; Older posts | Newer posts &rarr;* links with some page links?
This plugin provides the `wp_pagenavi()` template tag which generates fancy pagination links.
### Usage
In your theme, you need to find calls to next_posts_link() and previous_posts_link() and replace them.
In the Twentyten theme, it looks like this:
~~~
<div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">&larr;</span> Older posts', 'twentyten' ) ); ?></div>
<div class="nav-next"><?php previous_posts_link( __( 'Newer posts <span class="meta-nav">&rarr;</span>', 'twentyten' ) ); ?></div>
~~~
You would replace those two lines with this:
`<?php wp_pagenavi(); ?>`
For multipart pages, you would look for code like this:
`<?php wp_link_pages( ... ); ?>`
and replace it with this:
`<?php wp_pagenavi( array( 'type' => 'multipart' ) ); ?>`
Go to *WP-Admin -> Settings -> PageNavi* for configuration.
### Changing the CSS
If you need to configure the CSS style of WP-PageNavi, you can copy the `pagenavi-css.css` file from the plugin directory to your theme's directory and make your modifications there. This way, you won't lose your changes when you update the plugin.
Alternatively, you can uncheck the "Use pagenavi.css?" option from the settings page and add the styles to your theme's style.css file directly.
### Changing Class Names
There are [filters](http://codex.wordpress.org/Glossary#Filter) that can be used to change the default class names that are assigned to page navigation elements.
#### Filters
* `wp_pagenavi_class_pages`
* `wp_pagenavi_class_first`
* `wp_pagenavi_class_previouspostslink`
* `wp_pagenavi_class_extend`
* `wp_pagenavi_class_smaller`
* `wp_pagenavi_class_page`
* `wp_pagenavi_class_current`
* `wp_pagenavi_class_larger`
* `wp_pagenavi_class_nextpostslink`
* `wp_pagenavi_class_last`
#### Filter Usage
~~~
// Simple Usage - 1 callback per filter
add_filter('wp_pagenavi_class_previouspostslink', 'theme_pagination_previouspostslink_class');
add_filter('wp_pagenavi_class_nextpostslink', 'theme_pagination_nextpostslink_class');
add_filter('wp_pagenavi_class_page', 'theme_pagination_page_class');
function theme_pagination_previouspostslink_class($class_name) {
return 'pagination__control-link pagination__control-link--previous';
}
function theme_pagination_nextpostslink_class($class_name) {
return 'pagination__control-link pagination__control-link--next';
}
function theme_pagination_page_class($class_name) {
return 'pagination__current-page';
}
// More Concise Usage - 1 callback for all filters
add_filter('wp_pagenavi_class_previouspostslink', 'theme_pagination_class');
add_filter('wp_pagenavi_class_nextpostslink', 'theme_pagination_class');
add_filter('wp_pagenavi_class_page', 'theme_pagination_class');
function theme_pagination_class($class_name) {
switch($class_name) {
case 'previouspostslink':
$class_name = 'pagination__control-link pagination__control-link--previous';
break;
case 'nextpostslink':
$class_name = 'pagination__control-link pagination__control-link--next';
break;
case 'page':
$class_name = 'pagination__current'
break;
}
return $class_name;
}
~~~
### Development
* [https://github.com/lesterchan/wp-pagenavi](https://github.com/lesterchan/wp-pagenavi "https://github.com/lesterchan/wp-pagenavi")
### Credits
* Plugin icon by [SimpleIcon](http://www.simpleicon.com) from [Flaticon](http://www.flaticon.com)
### Donations
I spent most of my free time creating, updating, maintaining and supporting these plugins, if you really love my plugins and could spare me a couple of bucks, I will really appreciate it. If not feel free to use it without any obligations.
## Screenshots
1. With Custom Styling
2. Admin - Options Page
## Frequently Asked Questions
### Error on activation: "Parse error: syntax error, unexpected..."
Make sure your host is running PHP 5. The only foolproof way to do this is to add this line to wp-config.php (after the opening `<?php` tag):
`var_dump(PHP_VERSION);`
<br>
### When I go to page 2, I see the same posts as on page 1!
You're using `query_posts()` wrong. See [The Right Way To use query_posts()](http://scribu.net/wordpress/wp-pagenavi/right-way-to-use-query_posts.html)
### Does PageNavi work with secondary WP_Query instances?
Yes; read [this tutorial](http://scribu.net/wordpress/wp-pagenavi/wpn-2-74.html)
### How do I ignore the options page?
If you are running a multi-language plugin, you will probably want to ignore the strings in the options page.
You can do that like so:
`<?php wp_pagenavi( array( 'options' => PageNavi_Core::$options->get_defaults() ) ); ?>`
## Changelog
### 2.94.1
* FIXED: PHP 8.2 warnings
### 2.94.0
* NEW: Add args param on wp_pagenavi filter. Props @asadowski10
* NEW: Improve accessibility of nav links. Props @carlabobak
### 2.93.4
* FIXED: Update SCB Framework To Support PHP 8
### 2.93.3
* FIXED: Update SCB Framework To Remove contextual_help
### 2.93.2
* NEW: Bumped to WordPress 5.4
* FIXED: Ensure Action Links is always an array
### 2.93.1
* FIXED: Duplicated Settings Saved admin_notices
### 2.93
* Remove screen_icon from SCB.
### 2.92
* Add title attr to pages link. Props @Mahjouba91.
### 2.91
* Validate text option against kses
* Update SCB Framework
### 2.90
* Remove po/mo files from the plugin
* Use translate.wordpress.org to translate the plugin
### 2.89.1
* FIXED: before and after args
### 2.89
* NEW: wrapper_tag option to allow other HTML tag besides DIV and wrapper_class option to allow other class name besides wp-pagenavi. Props @Mahjouba91.
### 2.88
* NEW: Added filters for altering class names. Props @bookwyrm
### 2.87
* NEW: Uses WordPress native uninstall.php
### 2.86
* NEW: Bump to 4.0
* NEW: Added rel=next and rel=previous
### 2.85
* FIXED: "Use pagenavi-css.css" & "Always Show Page Navigation" in the options are not being saved
### 2.84
* FIXED: Updated scb framework to fix scbAdminPage incompatible error
### 2.83
* added 'echo' parameter
* added Estonian and Bengali translations
* updated scbFramework
### 2.82
* fixed prev/next links not appearing in some conditions
* added Hebrew, Georgian and Azerbaijani translations
* updated scbFramework
### 2.81
* require an explicit type; fixes bugs with multipart pages
### 2.80
* support for multi-part pages and user queries
* moved prev/next links before/after first/last links
* [more info](http://scribu.net/wordpress/wp-pagenavi/wpn-2-80.html)
### 2.74 (2011-02-17)
* added 'smaller' and 'larger' classes
* added $query arg to wp_pagenavi()
* updated translations
* [more info](http://scribu.net/wordpress/wp-pagenavi/wpn-2-74.html)

View File

@ -0,0 +1,563 @@
<?php
/**
* Administration page base class.
*/
abstract class scbAdminPage {
/** Page args
* $page_title string (mandatory)
* $parent (string) (default: options-general.php)
* $capability (string) (default: 'manage_options')
* $menu_title (string) (default: $page_title)
* $submenu_title (string) (default: $menu_title)
* $page_slug (string) (default: sanitized $page_title)
* $toplevel (string) If not empty, will create a new top level menu (for expected values see http://codex.wordpress.org/Administration_Menus#Using_add_submenu_page)
* - $icon_url (string) URL to an icon for the top level menu
* - $position (int) Position of the toplevel menu (caution!)
* $nonce string (default: $page_slug)
* $action_link (string|bool) Text of the action link on the Plugins page (default: 'Settings')
* $admin_action_priority int The priority that the admin_menu action should be executed at (default: 10)
*/
protected $args;
// URL to the current plugin directory.
// Useful for adding css and js files
protected $plugin_url;
// Created at page init
protected $pagehook;
// scbOptions object holder
// Normally, it's used for storing formdata
protected $options;
protected $option_name;
// l10n
protected $textdomain;
protected $nonce;
protected $file;
// ____________REGISTRATION COMPONENT____________
private static $registered = array();
/**
* Registers class of page.
*
* @param string $class
* @param string $file
* @param object $options (optional) A scbOptions object.
*
* @return bool
*/
public static function register( $class, $file, $options = null ) {
if ( isset( self::$registered[ $class ] ) ) {
return false;
}
self::$registered[ $class ] = array( $file, $options );
add_action( '_admin_menu', array( __CLASS__, '_pages_init' ) );
return true;
}
/**
* Replaces class of page.
*
* @param string $old_class
* @param string $new_class
*
* @return bool
*/
public static function replace( $old_class, $new_class ) {
if ( ! isset( self::$registered[ $old_class ] ) ) {
return false;
}
self::$registered[ $new_class ] = self::$registered[ $old_class ];
unset( self::$registered[ $old_class ] );
return true;
}
/**
* Removes class of page.
*
* @param string $class
*
* @return bool
*/
public static function remove( $class ) {
if ( ! isset( self::$registered[ $class ] ) ) {
return false;
}
unset( self::$registered[ $class ] );
return true;
}
/**
* Instantiates classes of pages.
*
* @return void
*/
public static function _pages_init() {
foreach ( self::$registered as $class => $args ) {
new $class( $args[0], $args[1] );
}
}
// ____________MAIN METHODS____________
/**
* Constructor.
*
* @param string|bool $file (optional)
* @param object $options (optional) A scbOptions object.
*
* @return void
*/
public function __construct( $file = false, $options = null ) {
if ( is_a( $options, 'scbOptions' ) ) {
$this->options = $options;
}
$this->setup();
$this->check_args();
if ( isset( $this->option_name ) ) {
add_action( 'admin_init', array( $this, 'option_init' ) );
}
add_action( 'admin_menu', array( $this, 'page_init' ), $this->args['admin_action_priority'] );
if ( $file ) {
$this->file = $file;
$this->plugin_url = plugin_dir_url( $file );
if ( $this->args['action_link'] ) {
add_filter( 'plugin_action_links_' . plugin_basename( $file ), array( $this, '_action_link' ) );
}
}
}
/**
* This is where all the page args can be set.
*
* @return void
*/
protected function setup() { }
/**
* Called when the page is loaded, but before any rendering.
* Useful for calling $screen->add_help_tab() etc.
*
* @return void
*/
public function page_loaded() {
$this->form_handler();
}
/**
* This is where the css and js go.
* Both wp_enqueue_*() and inline code can be added.
*
* @return void
*/
public function page_head() { }
/**
* This is where the contextual help goes.
*
* @return string
*/
protected function page_help() { }
/**
* A generic page header.
*
* @return void
*/
protected function page_header() {
echo "<div class='wrap'>\n";
echo html( 'h2', $this->args['page_title'] );
}
/**
* This is where the page content goes.
*
* @return void
*/
abstract protected function page_content();
/**
* A generic page footer.
*
* @return void
*/
protected function page_footer() {
echo "</div>\n";
}
/**
* This is where the form data should be validated.
*
* @param array $new_data
* @param array $old_data
*
* @return array
*/
public function validate( $new_data, $old_data ) {
return $new_data;
}
/**
* Manually handle option saving ( use Settings API instead ).
*
* @return bool
*/
protected function form_handler() {
if ( empty( $_POST['submit'] ) && empty( $_POST['action'] ) ) {
return false;
}
check_admin_referer( $this->nonce );
if ( ! isset( $this->options ) ) {
trigger_error( 'options handler not set', E_USER_WARNING );
return false;
}
$new_data = wp_array_slice_assoc( $_POST, array_keys( $this->options->get_defaults() ) );
$new_data = stripslashes_deep( $new_data );
$new_data = $this->validate( $new_data, $this->options->get() );
$this->options->set( $new_data );
add_action( 'admin_notices', array( $this, 'admin_msg' ) );
return true;
}
/**
* Manually generate a standard admin notice ( use Settings API instead ).
*
* @param string $msg (optional)
* @param string $class (optional)
*
* @return void
*/
public function admin_msg( $msg = '', $class = 'updated' ) {
if ( empty( $msg ) ) {
$msg = __( 'Settings <strong>saved</strong>.', $this->textdomain );
}
echo scb_admin_notice( $msg, $class );
}
// ____________UTILITIES____________
/**
* Generates a form submit button.
*
* @param string|array $value (optional) Button text or array of arguments.
* @param string $action (optional)
* @param string $class (optional)
*
* @return string
*/
public function submit_button( $value = '', $action = 'submit', $class = 'button' ) {
$args = is_array( $value ) ? $value : compact( 'value', 'action', 'class' );
$args = wp_parse_args( $args, array(
'value' => null,
'action' => $action,
'class' => $class,
) );
return get_submit_button( $args['value'], $args['class'], $args['action'] );
}
/**
* Mimics scbForms::form_wrap()
*
* $this->form_wrap( $content ); // generates a form with a default submit button
*
* $this->form_wrap( $content, false ); // generates a form with no submit button
*
* // the second argument is sent to submit_button()
* $this->form_wrap( $content, array(
* 'text' => 'Save changes',
* 'name' => 'action',
* ) );
*
* @see scbForms::form_wrap()
*
* @param string $content
* @param boolean|string|array $submit_button (optional)
*
* @return string
*/
public function form_wrap( $content, $submit_button = true ) {
if ( is_array( $submit_button ) ) {
$content .= $this->submit_button( $submit_button );
} else if ( true === $submit_button ) {
$content .= $this->submit_button();
} else if ( false !== strpos( $submit_button, '<input' ) ) {
$content .= $submit_button;
} else if ( false !== strpos( $submit_button, '<button' ) ) {
$content .= $submit_button;
} else if ( false !== $submit_button ) {
$button_args = array_slice( func_get_args(), 1 );
$content .= call_user_func_array( array( $this, 'submit_button' ), $button_args );
}
return scbForms::form_wrap( $content, $this->nonce );
}
/**
* Generates a table wrapped in a form.
*
* @param array $rows
* @param array|boolean $formdata (optional)
*
* @return string
*/
public function form_table( $rows, $formdata = false ) {
$output = '';
foreach ( $rows as $row ) {
$output .= $this->table_row( $row, $formdata );
}
$output = $this->form_table_wrap( $output );
return $output;
}
/**
* Wraps the given content in a <form><table>
*
* @param string $content
*
* @return string
*/
public function form_table_wrap( $content ) {
$output = $this->table_wrap( $content );
$output = $this->form_wrap( $output );
return $output;
}
/**
* Generates a form table.
*
* @param array $rows
* @param array|boolean $formdata (optional)
*
* @return string
*/
public function table( $rows, $formdata = false ) {
$output = '';
foreach ( $rows as $row ) {
$output .= $this->table_row( $row, $formdata );
}
$output = $this->table_wrap( $output );
return $output;
}
/**
* Generates a table row.
*
* @param array $args
* @param array|boolean $formdata (optional)
*
* @return string
*/
public function table_row( $args, $formdata = false ) {
return $this->row_wrap( $args['title'], $this->input( $args, $formdata ) );
}
/**
* Mimic scbForms inheritance.
*
* @see scbForms
*
* @param string $method
* @param array $args
*
* @return mixed
*/
public function __call( $method, $args ) {
if ( in_array( $method, array( 'input', 'form' ) ) ) {
if ( empty( $args[1] ) && isset( $this->options ) ) {
$args[1] = $this->options->get();
}
if ( 'form' == $method ) {
$args[2] = $this->nonce;
}
}
return call_user_func_array( array( 'scbForms', $method ), $args );
}
/**
* Wraps a string in a <script> tag.
*
* @param string $string
*
* @return string
*/
public function js_wrap( $string ) {
return html( "script type='text/javascript'", $string );
}
/**
* Wraps a string in a <style> tag.
*
* @param string $string
*
* @return string
*/
public function css_wrap( $string ) {
return html( "style type='text/css'", $string );
}
// ____________INTERNAL METHODS____________
/**
* Registers a page.
*
* @return void
*/
public function page_init() {
if ( ! $this->args['toplevel'] ) {
$this->pagehook = add_submenu_page(
$this->args['parent'],
$this->args['page_title'],
$this->args['menu_title'],
$this->args['capability'],
$this->args['page_slug'],
array( $this, '_page_content_hook' )
);
} else {
$func = 'add_' . $this->args['toplevel'] . '_page';
$this->pagehook = $func(
$this->args['page_title'],
$this->args['menu_title'],
$this->args['capability'],
$this->args['page_slug'],
null,
$this->args['icon_url'],
$this->args['position']
);
add_submenu_page(
$this->args['page_slug'],
$this->args['page_title'],
$this->args['submenu_title'],
$this->args['capability'],
$this->args['page_slug'],
array( $this, '_page_content_hook' )
);
}
if ( ! $this->pagehook ) {
return;
}
add_action( 'load-' . $this->pagehook, array( $this, 'page_loaded' ) );
add_action( 'admin_print_styles-' . $this->pagehook, array( $this, 'page_head' ) );
}
/**
* Registers a option.
*
* @return void
*/
public function option_init() {
register_setting( $this->option_name, $this->option_name, array( $this, 'validate' ) );
}
/**
* Checks page args.
*
* @return void
*/
private function check_args() {
if ( empty( $this->args['page_title'] ) ) {
trigger_error( 'Page title cannot be empty', E_USER_WARNING );
}
$this->args = wp_parse_args( $this->args, array(
'toplevel' => '',
'position' => null,
'icon_url' => '',
'parent' => 'options-general.php',
'capability' => 'manage_options',
'menu_title' => $this->args['page_title'],
'page_slug' => '',
'nonce' => '',
'action_link' => __( 'Settings', $this->textdomain ),
'admin_action_priority' => 10,
) );
if ( empty( $this->args['submenu_title'] ) ) {
$this->args['submenu_title'] = $this->args['menu_title'];
}
if ( empty( $this->args['page_slug'] ) ) {
$this->args['page_slug'] = sanitize_title_with_dashes( $this->args['menu_title'] );
}
if ( empty( $this->args['nonce'] ) ) {
$this->nonce = $this->args['page_slug'];
}
}
/**
* Displays page content.
*
* @return void
*/
public function _page_content_hook() {
$this->page_header();
$this->page_content();
$this->page_footer();
}
/**
* Adds an action link.
*
* @param array $links
*
* @return array
*/
public function _action_link( $links ) {
if ( ! is_array( $links ) ) {
$links = array();
}
$url = add_query_arg( 'page', $this->args['page_slug'], admin_url( $this->args['parent'] ) );
$links[] = html_link( $url, $this->args['action_link'] );
return $links;
}
}

View File

@ -0,0 +1,341 @@
<?php
/**
* Admin screen with metaboxes base class.
*/
abstract class scbBoxesPage extends scbAdminPage {
/*
A box definition looks like this:
array( $slug, $title, $column );
Available columns: normal, side, column3, column4
*/
protected $boxes = array();
/**
* Constructor.
*
* @param string|bool $file (optional)
* @param object $options (optional) A scbOptions object.
*
* @return void
*/
public function __construct( $file = false, $options = null ) {
parent::__construct( $file, $options );
scbUtil::add_uninstall_hook( $this->file, array( $this, 'uninstall' ) );
}
/**
* Registers a page.
*
* @return void
*/
public function page_init() {
if ( ! isset( $this->args['columns'] ) ) {
$this->args['columns'] = 2;
}
parent::page_init();
add_action( 'load-' . $this->pagehook, array( $this, 'boxes_init' ) );
}
/**
* Prints default CSS styles.
*
* @return void
*/
protected function default_css() {
?>
<style type="text/css">
.postbox-container + .postbox-container {
margin-left: 18px;
}
.postbox-container {
padding-right: 0;
}
.inside {
clear: both;
overflow: hidden;
}
.inside table {
margin: 0 !important;
padding: 0 !important;
}
.inside table td {
vertical-align: middle !important;
}
.inside table .regular-text {
width: 100% !important;
}
.inside .form-table th {
width: 30%;
max-width: 200px;
padding: 10px 0 !important;
}
.inside .widefat .check-column {
padding-bottom: 7px !important;
}
.inside p,
.inside table {
margin: 0 0 10px !important;
}
.inside p.submit {
float: left !important;
padding: 0 !important;
margin-bottom: 0 !important;
}
.meta-box-sortables {
min-height: 100px;
width: 100%;
}
</style>
<?php
}
/**
* Displays page content.
*
* @return void
*/
protected function page_content() {
$this->default_css();
global $screen_layout_columns;
if ( isset( $screen_layout_columns ) ) {
$hide2 = $hide3 = $hide4 = '';
switch ( $screen_layout_columns ) {
case 4:
if ( ! isset( $this->args['column_widths'] ) ) {
$this->args['column_widths'] = array( 24.5, 24.5, 24.5, 24.5 );
}
break;
case 3:
if ( ! isset( $this->args['column_widths'] ) ) {
$this->args['column_widths'] = array( 32.67, 32.67, 32.67 );
}
$hide4 = 'display:none;';
break;
case 2:
if ( ! isset( $this->args['column_widths'] ) ) {
$this->args['column_widths'] = array( 49, 49 );
}
$hide3 = $hide4 = 'display:none;';
break;
default:
if ( ! isset( $this->args['column_widths'] ) ) {
$this->args['column_widths'] = array( 98 );
}
$hide2 = $hide3 = $hide4 = 'display:none;';
}
$this->args['column_widths'] = array_pad( $this->args['column_widths'], 4, 0 );
}
?>
<div id='<?php echo $this->pagehook; ?>-widgets' class='metabox-holder'>
<?php
echo "\t<div class='postbox-container' style='width:{$this->args['column_widths'][0]}%'>\n";
do_meta_boxes( $this->pagehook, 'normal', '' );
echo "\t</div><div class='postbox-container' style='width:{$hide2}{$this->args['column_widths'][1]}%'>\n";
do_meta_boxes( $this->pagehook, 'side', '' );
echo "\t</div><div class='postbox-container' style='width:{$hide3}{$this->args['column_widths'][2]}%'>\n";
do_meta_boxes( $this->pagehook, 'column3', '' );
echo "\t</div><div class='postbox-container' style='width:{$hide4}{$this->args['column_widths'][3]}%'>\n";
do_meta_boxes( $this->pagehook, 'column4', '' );
?>
</div></div>
<?php
}
/**
* Displays page footer.
*
* @return void
*/
protected function page_footer() {
parent::page_footer();
$this->_boxes_js_init();
}
/**
* Handles option saving.
*
* @return void
*/
protected function form_handler() {
if ( empty( $_POST ) ) {
return;
}
check_admin_referer( $this->nonce );
// Box handler
foreach ( $this->boxes as $box ) {
$args = isset( $box[4] ) ? $box[4] : array();
$handler = $box[0] . '_handler';
if ( method_exists( $this, $handler ) ) {
call_user_func_array( array( $this, $handler ), $args );
}
}
}
/**
* Uninstalls boxes.
*
* @return void
*/
public function uninstall() {
global $wpdb;
$hook = str_replace( '-', '', $this->pagehook );
foreach ( array( 'metaboxhidden', 'closedpostboxes', 'wp_metaboxorder', 'screen_layout' ) as $option ) {
$keys[] = "'{$option}_{$hook}'";
}
$keys = '( ' . implode( ', ', $keys ) . ' )';
$wpdb->query( "
DELETE FROM {$wpdb->usermeta}
WHERE meta_key IN {$keys}
" );
}
/**
* Adds boxes.
*
* @return void
*/
public function boxes_init() {
wp_enqueue_script( 'postbox' );
add_screen_option( 'layout_columns', array(
'max' => $this->args['columns'],
'default' => $this->args['columns']
) );
$registered = array();
foreach ( $this->boxes as $box_args ) {
$box_args = self::numeric_to_assoc( $box_args, array( 'name', 'title', 'context', 'priority', 'args' ) );
$defaults = array(
'title' => ucfirst( $box_args['name'] ),
'context' => 'normal',
'priority' => 'default',
'args' => array()
);
$box_args = array_merge( $defaults, $box_args );
$name = $box_args['name'];
if ( isset( $registered[ $name ] ) ) {
if ( empty( $box_args['args'] ) ) {
trigger_error( "Duplicate box name: $name", E_USER_NOTICE );
}
$name = $this->_increment( $name );
} else {
$registered[ $name ] = true;
}
add_meta_box(
$name,
$box_args['title'],
array( $this, '_intermediate_callback' ),
$this->pagehook,
$box_args['context'],
$box_args['priority'],
$box_args['args']
);
}
}
/**
* Transforms numeric array to associative.
*
* @param array $argv
* @param array $keys
*
* @return array
*/
private static function numeric_to_assoc( $argv, $keys ) {
$args = array();
foreach ( $keys as $i => $key ) {
if ( isset( $argv[ $i ] ) ) {
$args[ $key ] = $argv[ $i ];
}
}
return $args;
}
/**
* Since we don't pass an object to do_meta_boxes(),
* pass $box['args'] directly to each method.
*
* @param string $_
* @param array $box
*
* @return void
*/
public function _intermediate_callback( $_, $box ) {
list( $name ) = explode( '-', $box['id'] );
call_user_func_array( array( $this, $name . '_box' ), $box['args'] );
}
/**
* Adds/Increments ID in box name.
*
* @param string $name
*
* @return string
*/
private function _increment( $name ) {
$parts = explode( '-', $name );
if ( isset( $parts[1] ) ) {
$parts[1]++;
} else {
$parts[1] = 2;
}
return implode( '-', $parts );
}
/**
* Adds necesary code for JS to work.
*
* @return void
*/
protected function _boxes_js_init() {
echo $this->js_wrap( <<<EOT
jQuery( document ).ready( function( $ ){
// close postboxes that should be closed
$( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
// postboxes setup
postboxes.add_postbox_toggles( '$this->pagehook' );
} );
EOT
);
?>
<form style='display: none' method='get' action=''>
<p>
<?php
wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
?>
</p>
</form>
<?php
}
}

View File

@ -0,0 +1,221 @@
<?php
/**
* WP Cron job container.
*/
class scbCron {
protected $schedule;
protected $interval;
protected $time;
protected $hook;
protected $callback_args = array();
/**
* Create a new cron job.
*
* @param array $args List of args:
* string $action OR callback $callback
* string $schedule OR number $interval
* array $callback_args (optional)
*
* @param string|bool $file (optional) Reference to main plugin file
*
* @return void
*/
public function __construct( $args, $file = false ) {
// Set time & schedule
if ( isset( $args['time'] ) ) {
$this->time = $args['time'];
}
if ( isset( $args['interval'] ) ) {
$this->schedule = $args['interval'] . 'secs';
$this->interval = $args['interval'];
} elseif ( isset( $args['schedule'] ) ) {
$this->schedule = $args['schedule'];
}
// Set hook
if ( isset( $args['action'] ) ) {
$this->hook = $args['action'];
} else if ( isset( $args['callback'] ) ) {
$this->hook = self::_callback_to_string( $args['callback'] );
add_action( $this->hook, $args['callback'] );
} else if ( method_exists( $this, 'callback' ) ) {
$this->hook = self::_callback_to_string( array( $this, 'callback' ) );
add_action( $this->hook, $args['callback'] );
} else {
trigger_error( '$action OR $callback not set', E_USER_WARNING );
}
if ( isset( $args['callback_args'] ) ) {
$this->callback_args = (array) $args['callback_args'];
}
if ( $file && $this->schedule ) {
scbUtil::add_activation_hook( $file, array( $this, 'reset' ) );
register_deactivation_hook( $file, array( $this, 'unschedule' ) );
}
add_filter( 'cron_schedules', array( $this, '_add_timing' ) );
}
/**
* Change the interval of the cron job.
*
* @param array $args List of args:
* string $schedule OR number $interval
* timestamp $time ( optional )
*
* @return void
*/
public function reschedule( $args ) {
if ( $args['schedule'] && $this->schedule != $args['schedule'] ) {
$this->schedule = $args['schedule'];
} elseif ( $args['interval'] && $this->interval != $args['interval'] ) {
$this->schedule = $args['interval'] . 'secs';
$this->interval = $args['interval'];
}
$this->time = $args['time'];
$this->reset();
}
/**
* Reset the schedule.
*
* @return void
*/
public function reset() {
$this->unschedule();
$this->schedule();
}
/**
* Clear the cron job.
*
* @return void
*/
public function unschedule() {
# wp_clear_scheduled_hook( $this->hook, $this->callback_args );
self::really_clear_scheduled_hook( $this->hook );
}
/**
* Execute the job now.
*
* @param array $args (optional) List of arguments to pass to the callback.
*
* @return void
*/
public function do_now( $args = null ) {
if ( is_null( $args ) ) {
$args = $this->callback_args;
}
do_action_ref_array( $this->hook, $args );
}
/**
* Execute the job with a given delay.
*
* @param int $delay (optional) Delay in seconds.
* @param array $args (optional) List of arguments to pass to the callback.
*
* @return void
*/
public function do_once( $delay = 0, $args = null ) {
if ( is_null( $args ) ) {
$args = $this->callback_args;
}
wp_clear_scheduled_hook( $this->hook, $args );
wp_schedule_single_event( time() + $delay, $this->hook, $args );
}
//_____INTERNAL METHODS_____
/**
* Adds custom schedule timing.
*
* @param array $schedules
*
* @return array
*/
public function _add_timing( $schedules ) {
if ( isset( $schedules[ $this->schedule ] ) ) {
return $schedules;
}
$schedules[ $this->schedule ] = array(
'interval' => $this->interval,
'display' => $this->interval . ' seconds',
);
return $schedules;
}
/**
* Schedule the job.
*
* @return void
*/
protected function schedule() {
if ( ! $this->time ) {
$this->time = time();
}
wp_schedule_event( $this->time, $this->schedule, $this->hook, $this->callback_args );
}
/**
* Clears scheduled job.
*
* @param string $name
*
* @return void
*/
protected static function really_clear_scheduled_hook( $name ) {
$crons = _get_cron_array();
foreach ( $crons as $timestamp => $hooks ) {
foreach ( $hooks as $hook => $args ) {
if ( $hook == $name ) {
unset( $crons[ $timestamp ][ $hook ] );
}
}
if ( empty( $crons[ $timestamp ] ) ) {
unset( $crons[ $timestamp ] );
}
}
_set_cron_array( $crons );
}
/**
* Generates a hook name from given callback.
*
* @param callback $callback
*
* @return string
*/
protected static function _callback_to_string( $callback ) {
if ( ! is_array( $callback ) ) {
$str = $callback;
} else if ( ! is_string( $callback[0] ) ) {
$str = get_class( $callback[0] ) . '_' . $callback[1];
} else {
$str = $callback[0] . '::' . $callback[1];
}
$str .= '_hook';
return $str;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
<?php
/**
* Automatic filter binding.
*/
class scbHooks {
private static $mangle_name;
/**
* Adds.
*
* @param string $class
*
* @return void
*/
public static function add( $class ) {
self::_do( 'add_filter', $class );
}
/**
* Removes.
*
* @param string $class
*
* @return void
*/
public static function remove( $class ) {
self::_do( 'remove_filter', $class );
}
/**
* Prints debug.
*
* @param string $class
* @param string $mangle_name (optional)
*
* @return void
*/
public static function debug( $class, $mangle_name = false ) {
self::$mangle_name = $mangle_name;
echo "<pre>";
self::_do( array( __CLASS__, '_print' ), $class );
echo "</pre>";
}
/**
* Prints.
*
* @param string $tag
* @param array $callback
* @param int $prio
* @param int $argc
*
* @return void
*/
private static function _print( $tag, $callback, $prio, $argc ) {
$static = ! is_object( $callback[0] );
if ( self::$mangle_name ) {
$class = $static ? '__CLASS__' : '$this';
} else if ( $static ) {
$class = "'" . $callback[0] . "'";
} else {
$class = '$' . get_class( $callback[0] );
}
$func = "array( $class, '$callback[1]' )";
echo "add_filter( '$tag', $func";
if ( $prio != 10 || $argc > 1 ) {
echo ", $prio";
if ( $argc > 1 ) {
echo ", $argc";
}
}
echo " );\n";
}
/**
* Processes.
*
* @param string $action
* @param string $class
*
* @return void
*/
private static function _do( $action, $class ) {
$reflection = new ReflectionClass( $class );
foreach ( $reflection->getMethods() as $method ) {
if ( $method->isPublic() && ! $method->isConstructor() ) {
$comment = $method->getDocComment();
if ( preg_match( '/@nohook[ \t\*\n]+/', $comment ) ) {
continue;
}
preg_match_all( '/@hook:?\s+([^\s]+)/', $comment, $matches ) ? $matches[1] : $method->name;
if ( empty( $matches[1] ) ) {
$hooks = array( $method->name );
} else {
$hooks = $matches[1];
}
$priority = preg_match( '/@priority:?\s+(\d+)/', $comment, $matches ) ? $matches[1] : 10;
foreach ( $hooks as $hook ) {
call_user_func( $action, $hook, array( $class, $method->name ), $priority, $method->getNumberOfParameters() );
}
}
}
}
}

View File

@ -0,0 +1,197 @@
<?php
/**
* Container for an array of options.
*/
class scbOptions {
/**
* The option name.
* @var string
*/
protected $key;
/**
* The default values.
* @var array
*/
protected $defaults;
/**
* Used by WP hooks.
* @var null
*/
public $wp_filter_id;
/**
* Create a new set of options.
*
* @param string $key Option name.
* @param string $file Reference to main plugin file.
* @param array $defaults (optional) An associative array of default values.
*
* @return void
*/
public function __construct( $key, $file, $defaults = array() ) {
$this->key = $key;
$this->defaults = $defaults;
if ( $file ) {
scbUtil::add_activation_hook( $file, array( $this, '_activation' ) );
scbUtil::add_uninstall_hook( $file, array( $this, 'delete' ) );
}
}
/**
* Returns option name.
*
* @return string
*/
public function get_key() {
return $this->key;
}
/**
* Get option values for one or all fields.
*
* @param string|array $field (optional) The field to get.
* @param mixed $default (optional) The value returned when the key is not found.
*
* @return mixed Whatever is in those fields.
*/
public function get( $field = null, $default = null ) {
$current_options = get_option( $this->key, array() );
$data = array_merge( $this->defaults, is_array( $current_options ) ? $current_options : array() );
return scbForms::get_value( $field, $data, $default );
}
/**
* Get default values for one or all fields.
*
* @param string|array $field (optional) The field to get.
*
* @return mixed Whatever is in those fields.
*/
public function get_defaults( $field = null ) {
return scbForms::get_value( $field, $this->defaults );
}
/**
* Set all data fields, certain fields or a single field.
*
* @param string|array $field The field to update or an associative array.
* @param mixed $value (optional) The new value ( ignored if $field is array ).
*
* @return void
*/
public function set( $field, $value = '' ) {
if ( is_array( $field ) ) {
$newdata = $field;
} else {
$newdata = array( $field => $value );
}
$this->update( array_merge( $this->get(), $newdata ) );
}
/**
* Reset option to defaults.
*
* @return void
*/
public function reset() {
$this->update( $this->defaults, false );
}
/**
* Remove any keys that are not in the defaults array.
*
* @return void
*/
public function cleanup() {
$this->update( $this->get(), true );
}
/**
* Update raw data.
*
* @param mixed $newdata
* @param bool $clean (optional) Whether to remove unrecognized keys or not.
*
* @return void
*/
public function update( $newdata, $clean = true ) {
if ( $clean ) {
$newdata = $this->_clean( $newdata );
}
update_option( $this->key, array_merge( $this->get(), $newdata ) );
}
/**
* Delete the option.
*
* @return void
*/
public function delete() {
delete_option( $this->key );
}
//_____INTERNAL METHODS_____
/**
* Saves an extra query.
*
* @return void
*/
public function _activation() {
add_option( $this->key, $this->defaults );
}
/**
* Keep only the keys defined in $this->defaults
*
* @param array $data
*
* @return array
*/
private function _clean( $data ) {
return wp_array_slice_assoc( $data, array_keys( $this->defaults ) );
}
private function &_get( $field, $data ) {
}
/**
* Magic method: $options->field
*
* @param string|array $field The field to get.
*
* @return mixed
*/
public function __get( $field ) {
return $this->get( $field );
}
/**
* Magic method: $options->field = $value
*
* @return void
*/
public function __set( $field, $value ) {
$this->set( $field, $value );
}
/**
* Magic method: isset( $options->field )
*
* @return bool
*/
public function __isset( $field ) {
$data = $this->get();
return isset( $data[ $field ] );
}
}

View File

@ -0,0 +1,346 @@
<?php
/**
* Class that creates metaboxes on the post editing page.
*/
class scbPostMetabox {
/**
* Metabox ID.
* @var string
*/
private $id;
/**
* Title.
* @var string
*/
private $title;
/**
* Post types.
* @var array
*/
private $post_types;
/**
* Post meta data.
* @var array
*/
private $post_data = array();
/**
* Action hooks.
* @var array
*/
protected $actions = array( 'admin_enqueue_scripts', 'post_updated_messages' );
/**
* Sets up metabox.
*
* @param string $id
* @param string $title
* @param array $args (optional)
*
* @return void
*/
public function __construct( $id, $title, $args = array() ) {
$this->id = $id;
$this->title = $title;
$args = wp_parse_args( $args, array(
'post_type' => 'post',
'context' => 'advanced',
'priority' => 'default',
) );
if ( is_string( $args['post_type'] ) ) {
$args['post_type'] = array( $args['post_type'] );
}
$this->post_types = $args['post_type'];
$this->context = $args['context'];
$this->priority = $args['priority'];
add_action( 'load-post.php', array( $this, 'pre_register' ) );
add_action( 'load-post-new.php', array( $this, 'pre_register' ) );
}
/**
* Pre register the metabox.
*
* @return void
*/
final public function pre_register() {
if ( ! in_array( get_current_screen()->post_type, $this->post_types ) ) {
return;
}
if ( ! $this->condition() ) {
return;
}
if ( isset( $_GET['post'] ) ) {
$this->post_data = $this->get_meta( intval( $_GET['post'] ) );
}
add_action( 'add_meta_boxes', array( $this, 'register' ) );
add_action( 'save_post', array( $this, '_save_post' ), 10, 2 );
foreach ( $this->actions as $action ) {
if ( method_exists( $this, $action ) ) {
add_action( $action, array( $this, $action ) );
}
}
}
/**
* Additional checks before registering the metabox.
*
* @return bool
*/
protected function condition() {
return true;
}
/**
* Registers the metabox.
*
* @return void
*/
final public function register() {
add_meta_box( $this->id, $this->title, array( $this, 'display' ), null, $this->context, $this->priority );
}
/**
* Filter data before display.
*
* @param array $form_data
* @param object $post
*
* @return array
*/
public function before_display( $form_data, $post ) {
return $form_data;
}
/**
* Displays metabox content.
*
* @param object $post
*
* @return void
*/
public function display( $post ) {
$form_fields = $this->form_fields();
if ( ! $form_fields ) {
return;
}
$form_data = $this->post_data;
$error_fields = array();
if ( isset( $form_data['_error_data_' . $this->id ] ) ) {
$data = maybe_unserialize( $form_data['_error_data_' . $this->id ] );
$error_fields = $data['fields'];
$form_data = $data['data'];
$this->display_notices( $data['messages'], 'error' );
}
$form_data = $this->before_display( $form_data, $post );
$this->before_form( $post );
echo $this->table( $form_fields, $form_data, $error_fields );
$this->after_form( $post );
delete_post_meta( $post->ID, '_error_data_' . $this->id );
}
/**
* Returns table.
*
* @param array $rows
* @param array $formdata
* @param array $errors (optional)
*
* @return string
*/
public function table( $rows, $formdata, $errors = array() ) {
$output = '';
foreach ( $rows as $row ) {
$output .= $this->table_row( $row, $formdata, $errors );
}
$output = scbForms::table_wrap( $output );
return $output;
}
/**
* Returns table row.
*
* @param array $row
* @param array $formdata
* @param array $errors (optional)
*
* @return string
*/
public function table_row( $row, $formdata, $errors = array() ) {
$input = scbForms::input( $row, $formdata );
// If row has an error, highlight it
$style = ( in_array( $row['name'], $errors ) ) ? 'style="background-color: #FFCCCC"' : '';
return html( 'tr',
html( "th $style scope='row'", $row['title'] ),
html( "td $style", $input )
);
}
/**
* Displays notices.
*
* @param array|string $notices
* @param string $class (optional)
*
* @return void
*/
public function display_notices( $notices, $class = 'updated' ) {
// add inline class so the notices stays in metabox
$class .= ' inline';
foreach ( (array) $notices as $notice ) {
echo scb_admin_notice( $notice, $class );
}
}
/**
* Display some extra HTML before the form.
*
* @param object $post
*
* @return void
*/
public function before_form( $post ) { }
/**
* Return an array of form fields.
*
* @return array
*/
public function form_fields() {
return array();
}
/**
* Display some extra HTML after the form.
*
* @param object $post
*
* @return void
*/
public function after_form( $post ) { }
/**
* Makes sure that the saving occurs only for the post being edited.
*
* @param int $post_id
* @param object $post
*
* @return void
*/
final public function _save_post( $post_id, $post ) {
if ( ! isset( $_POST['action'] ) || $_POST['action'] != 'editpost' ) {
return;
}
if ( ! isset( $_POST['post_ID'] ) || $_POST['post_ID'] != $post_id ) {
return;
}
if ( ! in_array( $post->post_type, $this->post_types ) ) {
return;
}
$this->save( $post->ID );
}
/**
* Saves metabox form data.
*
* @param int $post_id
*
* @return void
*/
protected function save( $post_id ) {
$form_fields = $this->form_fields();
$to_update = scbForms::validate_post_data( $form_fields );
// Filter data
$to_update = $this->before_save( $to_update, $post_id );
// Validate dataset
$is_valid = $this->validate_post_data( $to_update, $post_id );
if ( $is_valid instanceof WP_Error && $is_valid->get_error_codes() ) {
$error_data = array(
'fields' => $is_valid->get_error_codes(),
'messages' => $is_valid->get_error_messages(),
'data' => $to_update,
);
update_post_meta( $post_id, '_error_data_' . $this->id, $error_data );
$location = add_query_arg( 'message', 1, get_edit_post_link( $post_id, 'url' ) );
wp_redirect( esc_url_raw( apply_filters( 'redirect_post_location', $location, $post_id ) ) );
exit;
}
foreach ( $to_update as $key => $value ) {
update_post_meta( $post_id, $key, $value );
}
}
/**
* Filter data before save.
*
* @param array $post_data
* @param int $post_id
*
* @return array
*/
protected function before_save( $post_data, $post_id ) {
return $post_data;
}
/**
* Validate posted data.
*
* @param array $post_data
* @param int $post_id
*
* @return bool|object A WP_Error object if posted data are invalid.
*/
protected function validate_post_data( $post_data, $post_id ) {
return false;
}
/**
* Returns an array of post meta.
*
* @param int $post_id
*
* @return array
*/
private function get_meta( $post_id ) {
$meta = get_post_custom( $post_id );
foreach ( $meta as $key => $values ) {
$meta[ $key ] = maybe_unserialize( $meta[ $key ][0] );
}
return $meta;
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* Takes care of creating, updating and deleting database tables.
*/
class scbTable {
/**
* The table name.
* @var string
*/
protected $name;
/**
* The table columns.
* @var string
*/
protected $columns;
/**
* The upgrade method.
* @var string
*/
protected $upgrade_method;
/**
* Sets up table.
*
* @param string $name Table name.
* @param string $file Reference to main plugin file.
* @param string $columns The SQL columns for the CREATE TABLE statement.
* @param array $upgrade_method (optional)
*
* @return void
*/
public function __construct( $name, $file, $columns, $upgrade_method = 'dbDelta' ) {
$this->name = $name;
$this->columns = $columns;
$this->upgrade_method = $upgrade_method;
scb_register_table( $name );
if ( $file ) {
scbUtil::add_activation_hook( $file, array( $this, 'install' ) );
scbUtil::add_uninstall_hook( $file, array( $this, 'uninstall' ) );
}
}
/**
* Installs table.
*
* @return void
*/
public function install() {
scb_install_table( $this->name, $this->columns, $this->upgrade_method );
}
/**
* Uninstalls table.
*
* @return void
*/
public function uninstall() {
scb_uninstall_table( $this->name );
}
}

View File

@ -0,0 +1,476 @@
<?php
/**
* Various utilities.
*/
class scbUtil {
/**
* Force script enqueue.
*
* @param array $handles
*
* @return void
*/
public static function do_scripts( $handles ) {
global $wp_scripts;
if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) {
$wp_scripts = new WP_Scripts();
}
$wp_scripts->do_items( ( array ) $handles );
}
/**
* Force style enqueue.
*
* @param array $handles
*
* @return void
*/
public static function do_styles( $handles ) {
self::do_scripts( 'jquery' );
global $wp_styles;
if ( ! is_a( $wp_styles, 'WP_Styles' ) ) {
$wp_styles = new WP_Styles();
}
ob_start();
$wp_styles->do_items( ( array ) $handles );
$content = str_replace( array( "'", "\n" ), array( '"', '' ), ob_get_clean() );
echo "<script type='text/javascript'>\n";
echo "//<![CDATA[";
echo "jQuery(function ($) { $('head').prepend('$content'); });\n";
echo "//]]>";
echo "</script>";
}
/**
* Enable delayed plugin activation. To be used with scb_init()
*
* @param string $plugin
* @param string|array $callback
*
* @return void
*/
public static function add_activation_hook( $plugin, $callback ) {
if ( defined( 'SCB_LOAD_MU' ) ) {
register_activation_hook( $plugin, $callback );
} else {
add_action( 'scb_activation_' . plugin_basename( $plugin ), $callback );
}
}
/**
* Execute activation hook.
* For debugging.
*
* @param string $plugin
*
* @return void
*/
public static function do_activation( $plugin ) {
do_action( 'scb_activation_' . plugin_basename( $plugin ) );
}
/**
* Allows more than one uninstall hooks.
* Also prevents an UPDATE query on each page load.
*
* @param string $plugin
* @param string|array $callback
*
* @return void
*/
public static function add_uninstall_hook( $plugin, $callback ) {
if ( ! is_admin() ) {
return;
}
register_uninstall_hook( $plugin, '__return_false' ); // dummy
add_action( 'uninstall_' . plugin_basename( $plugin ), $callback );
}
/**
* Execute uninstall hook.
* For debugging.
*
* @param string $plugin
*
* @return void
*/
public static function do_uninstall( $plugin ) {
do_action( 'uninstall_' . plugin_basename( $plugin ) );
}
/**
* Get the current, full URL.
*
* @return string
*/
public static function get_current_url() {
return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
/**
* Apply a function to each element of a ( nested ) array recursively.
*
* @param string|array $callback
* @param array $array
*
* @return array
*/
public static function array_map_recursive( $callback, $array ) {
array_walk_recursive( $array, array( __CLASS__, 'array_map_recursive_helper' ), $callback );
return $array;
}
public static function array_map_recursive_helper( &$val, $key, $callback ) {
$val = call_user_func( $callback, $val );
}
/**
* Extract certain $keys from $array.
*
* @deprecated WP 3.1
* @deprecated Use wp_array_slice_assoc()
* @see wp_array_slice_assoc()
*
* @param array $array
* @param array $keys
*
* @return array
*/
public static function array_extract( $array, $keys ) {
_deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'WP 3.1', 'wp_array_slice_assoc()' );
return wp_array_slice_assoc( $array, $keys );
}
/**
* Extract a certain value from a list of arrays.
*
* @deprecated WP 3.1
* @deprecated Use wp_list_pluck()
* @see wp_list_pluck()
*
* @param array $array
* @param string $key
*
* @return array
*/
public static function array_pluck( $array, $key ) {
_deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'WP 3.1', 'wp_list_pluck()' );
return wp_list_pluck( $array, $key );
}
/**
* Transform a list of objects into an associative array.
*
* @deprecated r41
* @deprecated Use scb_list_fold()
* @see scb_list_fold()
*
* @param array $objects
* @param string $key
* @param string $value
*
* @return array
*/
public static function objects_to_assoc( $objects, $key, $value ) {
_deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'r41', 'scb_list_fold()' );
return scb_list_fold( $objects, $key, $value );
}
/**
* Prepare an array for an IN statement.
*
* @param array $values
*
* @return string
*/
public static function array_to_sql( $values ) {
foreach ( $values as &$val ) {
$val = "'" . esc_sql( trim( $val ) ) . "'";
}
return implode( ',', $values );
}
/**
* Example: split_at( '</', '<a></a>' ) => array( '<a>', '</a>' )
*
* @param string $delim
* @param string $str
*
* @return array
*/
public static function split_at( $delim, $str ) {
$i = strpos( $str, $delim );
if ( false === $i ) {
return false;
}
$start = substr( $str, 0, $i );
$finish = substr( $str, $i );
return array( $start, $finish );
}
}
/**
* Return a standard admin notice.
*
* @param string $msg
* @param string $class (optional)
*
* @return string
*/
function scb_admin_notice( $msg, $class = 'updated' ) {
return html( "div class='$class fade'", html( "p", $msg ) );
}
/**
* Transform a list of objects into an associative array.
*
* @param array $objects
* @param string $key
* @param string $value
*
* @return array
*/
function scb_list_fold( $list, $key, $value ) {
$r = array();
if ( is_array( reset( $list ) ) ) {
foreach ( $list as $item ) {
$r[ $item[ $key ] ] = $item[ $value ];
}
} else {
foreach ( $list as $item ) {
$r[ $item->$key ] = $item->$value;
}
}
return $r;
}
/**
* Splits a list into sets, grouped by the result of running each value through $fn.
*
* @param array $list List of items to be partitioned.
* @param callback $fn Function that takes an element and returns a string key.
*
* @return array
*/
function scb_list_group_by( $list, $fn ) {
$groups = array();
foreach ( $list as $item ) {
$key = call_user_func( $fn, $item );
if ( null === $key ) {
continue;
}
$groups[ $key ][] = $item;
}
return $groups;
}
//_____Database Table Utilities_____
/**
* Register a table with $wpdb.
*
* @param string $key The key to be used on the $wpdb object.
* @param string $name (optional) The actual name of the table, without $wpdb->prefix.
*
* @return void
*/
function scb_register_table( $key, $name = false ) {
global $wpdb;
if ( ! $name ) {
$name = $key;
}
$wpdb->tables[] = $name;
$wpdb->$key = $wpdb->prefix . $name;
}
/**
* Runs the SQL query for installing/upgrading a table.
*
* @param string $key The key used in scb_register_table().
* @param string $columns The SQL columns for the CREATE TABLE statement.
* @param array $opts (optional) Various other options.
*
* @return void
*/
function scb_install_table( $key, $columns, $opts = array() ) {
global $wpdb;
$full_table_name = $wpdb->$key;
if ( is_string( $opts ) ) {
$opts = array( 'upgrade_method' => $opts );
}
$opts = wp_parse_args( $opts, array(
'upgrade_method' => 'dbDelta',
'table_options' => '',
) );
$charset_collate = '';
if ( $wpdb->has_cap( 'collation' ) ) {
if ( ! empty( $wpdb->charset ) ) {
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collate .= " COLLATE $wpdb->collate";
}
}
$table_options = $charset_collate . ' ' . $opts['table_options'];
if ( 'dbDelta' == $opts['upgrade_method'] ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( "CREATE TABLE $full_table_name ( $columns ) $table_options" );
return;
}
if ( 'delete_first' == $opts['upgrade_method'] ) {
$wpdb->query( "DROP TABLE IF EXISTS $full_table_name;" );
}
$wpdb->query( "CREATE TABLE IF NOT EXISTS $full_table_name ( $columns ) $table_options;" );
}
/**
* Runs the SQL query for uninstalling a table.
*
* @param string $key The key used in scb_register_table().
*
* @return void
*/
function scb_uninstall_table( $key ) {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->$key );
}
//_____Minimalist HTML framework_____
/**
* Generate an HTML tag. Atributes are escaped. Content is NOT escaped.
*
* @param string $tag
*
* @return string
*/
if ( ! function_exists( 'html' ) ):
function html( $tag ) {
static $SELF_CLOSING_TAGS = array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta' );
$args = func_get_args();
$tag = array_shift( $args );
if ( is_array( $args[0] ) ) {
$closing = $tag;
$attributes = array_shift( $args );
foreach ( $attributes as $key => $value ) {
if ( false === $value ) {
continue;
}
if ( true === $value ) {
$value = $key;
}
$tag .= ' ' . $key . '="' . esc_attr( $value ) . '"';
}
} else {
list( $closing ) = explode( ' ', $tag, 2 );
}
if ( in_array( $closing, $SELF_CLOSING_TAGS ) ) {
return "<{$tag} />";
}
$content = implode( '', $args );
return "<{$tag}>{$content}</{$closing}>";
}
endif;
/**
* Generate an <a> tag.
*
* @param string $url
* @param string $title (optional)
*
* @return string
*/
if ( ! function_exists( 'html_link' ) ):
function html_link( $url, $title = '' ) {
if ( empty( $title ) ) {
$title = $url;
}
return html( 'a', array( 'href' => esc_url( $url ) ), $title );
}
endif;
/**
* Returns an array of query flags.
*
* @param object $wp_query (optional)
*
* @return array
*/
function scb_get_query_flags( $wp_query = null ) {
if ( ! $wp_query ) {
$wp_query = $GLOBALS['wp_query'];
}
$flags = array();
foreach ( get_object_vars( $wp_query ) as $key => $val ) {
if ( 'is_' == substr( $key, 0, 3 ) && $val ) {
$flags[] = substr( $key, 3 );
}
}
return $flags;
}
//_____Compatibility layer_____
/**
* Update data from a post field based on Post ID.
* @see https://core.trac.wordpress.org/ticket/10946
*
* @param string $field Post field name.
* @param string $value Post field value.
* @param int $post_id Post ID.
*
* @return bool Result of UPDATE query.
*/
if ( ! function_exists( 'set_post_field' ) ) :
function set_post_field( $field, $value, $post_id ) {
global $wpdb;
$post_id = absint( $post_id );
$value = sanitize_post_field( $field, $value, $post_id, 'db' );
return $wpdb->update( $wpdb->posts, array( $field => $value ), array( 'ID' => $post_id ) );
}
endif;

View File

@ -0,0 +1,124 @@
<?php
/**
* Adds compatibility methods between WP_Widget and scbForms.
*/
abstract class scbWidget extends WP_Widget {
/**
* Widget defaults.
* @var array
*/
protected $defaults = array();
/**
* Widgets to register.
* @var array
*/
private static $scb_widgets = array();
/**
* Initializes widget.
*
* @param string $class
* @param string $file (optional)
* @param string $base (optional)
*
* @return void
*/
public static function init( $class, $file = '', $base = '' ) {
self::$scb_widgets[] = $class;
add_action( 'widgets_init', array( __CLASS__, '_scb_register' ) );
// for auto-uninstall
if ( $file && $base && class_exists( 'scbOptions' ) ) {
new scbOptions( "widget_$base", $file );
}
}
/**
* Registers widgets.
*
* @return void
*/
public static function _scb_register() {
foreach ( self::$scb_widgets as $widget ) {
register_widget( $widget );
}
}
/**
* Displays widget content.
*
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
* @param array $instance The settings for the particular instance of the widget.
*
* @return void
*/
public function widget( $args, $instance ) {
$instance = wp_parse_args( $instance, $this->defaults );
extract( $args );
echo $before_widget;
$title = apply_filters( 'widget_title', isset( $instance['title'] ) ? $instance['title'] : '', $instance, $this->id_base );
if ( ! empty( $title ) ) {
echo $before_title . $title . $after_title;
}
$this->content( $instance );
echo $after_widget;
}
/**
* This is where the actual widget content goes.
*
* @param array $instance The settings for the particular instance of the widget.
*
* @return void
*/
protected function content( $instance ) { }
//_____HELPER METHODS_____
/**
* Generates a input form field.
*
* @param array $args
* @param array $formdata (optional)
*
* @return string
*/
protected function input( $args, $formdata = array() ) {
$prefix = array( 'widget-' . $this->id_base, $this->number );
$form = new scbForm( $formdata, $prefix );
// Add default class
if ( ! isset( $args['extra'] ) && 'text' == $args['type'] ) {
$args['extra'] = array( 'class' => 'widefat' );
}
// Add default label position
if ( ! in_array( $args['type'], array( 'checkbox', 'radio' ) ) && empty( $args['desc_pos'] ) ) {
$args['desc_pos'] = 'before';
}
$name = $args['name'];
if ( ! is_array( $name ) && '[]' == substr( $name, -2 ) ) {
$name = array( substr( $name, 0, -2 ), '' );
}
$args['name'] = $name;
return $form->input( $args );
}
}

View File

@ -0,0 +1,22 @@
{
"name" : "scribu/scb-framework",
"description": "A set of useful classes for faster plugin development",
"keywords" : ["wordpress"],
"homepage" : "https://github.com/scribu/wp-scb-framework",
"license" : "GPL-3.0+",
"authors" : [
{
"name" : "Cristi Burcă",
"homepage": "http://scribu.net/"
}
],
"support" : {
"issues": "https://github.com/scribu/wp-scb-framework/issues",
"source": "https://github.com/scribu/wp-scb-framework",
"wiki": "https://github.com/scribu/wp-scb-framework/wiki"
},
"autoload" : {
"classmap": ["."],
"files" : ["load-composer.php", "Util.php"]
}
}

View File

@ -0,0 +1,12 @@
<?php
/**
* Pass through version to use when Composer handles classes load.
*
* @param callable $callback
*/
function scb_init( $callback = null ) {
if ( $callback ) {
call_user_func( $callback );
}
}

View File

@ -0,0 +1,105 @@
<?php
$GLOBALS['_scb_data'] = array( 61, __FILE__, array(
'scbUtil',
'scbOptions',
'scbForms',
'scbTable',
'scbWidget',
'scbAdminPage',
'scbBoxesPage',
'scbPostMetabox',
'scbCron',
'scbHooks',
) );
if ( ! class_exists( 'scbLoad4' ) ) :
/**
* The main idea behind this class is to load the most recent version of the scb classes available.
*
* It waits until all plugins are loaded and then does some crazy hacks to make activation hooks work.
*/
class scbLoad4 {
private static $candidates = array();
private static $classes;
private static $callbacks = array();
private static $loaded;
static function init( $callback = '' ) {
list( $rev, $file, $classes ) = $GLOBALS['_scb_data'];
self::$candidates[ $file ] = $rev;
self::$classes[ $file ] = $classes;
if ( ! empty( $callback ) ) {
self::$callbacks[ $file ] = $callback;
add_action( 'activate_plugin', array( __CLASS__, 'delayed_activation' ) );
}
if ( did_action( 'plugins_loaded' ) ) {
self::load();
} else {
add_action( 'plugins_loaded', array( __CLASS__, 'load' ), 9, 0 );
}
}
public static function delayed_activation( $plugin ) {
$plugin_dir = dirname( $plugin );
if ( '.' == $plugin_dir ) {
return;
}
foreach ( self::$callbacks as $file => $callback ) {
if ( dirname( dirname( plugin_basename( $file ) ) ) == $plugin_dir ) {
self::load( false );
call_user_func( $callback );
do_action( 'scb_activation_' . $plugin );
break;
}
}
}
public static function load( $do_callbacks = true ) {
arsort( self::$candidates );
$file = key( self::$candidates );
$path = dirname( $file ) . '/';
foreach ( self::$classes[ $file ] as $class_name ) {
if ( class_exists( $class_name ) ) {
continue;
}
$fpath = $path . substr( $class_name, 3 ) . '.php';
if ( file_exists( $fpath ) ) {
include $fpath;
self::$loaded[] = $fpath;
}
}
if ( $do_callbacks ) {
foreach ( self::$callbacks as $callback ) {
call_user_func( $callback );
}
}
}
static function get_info() {
arsort( self::$candidates );
return array( self::$loaded, self::$candidates );
}
}
endif;
if ( ! function_exists( 'scb_init' ) ) :
function scb_init( $callback = '' ) {
scbLoad4::init( $callback );
}
endif;

View File

@ -0,0 +1,24 @@
<?php
// Template Name: WPN Debug
get_header();
query_posts( array( 'post_type' => 'post', 'paged' => get_query_var( 'paged' ) ) );
?>
<div id="primary">
<div id="content" role="main">
<ul>
<?php while ( have_posts() ) : the_post(); ?>
<li><?php the_title(); ?>
<?php endwhile?>
</ul>
<?php wp_pagenavi(); ?>
<?php echo wp_pagenavi( array( 'echo' => false ) ); ?>
</div><!-- #content -->
</div><!-- #primary -->
<?php get_footer(); ?>

View File

@ -0,0 +1,47 @@
<?php
/*
* Uninstall plugin
*/
if ( !defined( 'WP_UNINSTALL_PLUGIN' ) )
exit ();
$option_names = array(
'pagenavi_options'
);
if ( is_multisite() ) {
$ms_sites = function_exists( 'get_sites' ) ? get_sites() : wp_get_sites();
if( 0 < sizeof( $ms_sites ) ) {
foreach ( $ms_sites as $ms_site ) {
$blog_id = isset( $ms_site['blog_id'] ) ? $ms_site['blog_id'] : $ms_site->blog_id;
switch_to_blog( $blog_id );
if( sizeof( $option_names ) > 0 ) {
foreach( $option_names as $option_name ) {
delete_option( $option_name );
plugin_uninstalled();
}
}
}
}
restore_current_blog();
} else {
if( sizeof( $option_names ) > 0 ) {
foreach( $option_names as $option_name ) {
delete_option( $option_name );
plugin_uninstalled();
}
}
}
/**
* Delete plugin table when uninstalled
*
* @access public
* @return void
*/
function plugin_uninstalled() {
global $wpdb;
}

View File

@ -0,0 +1,45 @@
<?php
/*
Plugin Name: WP-PageNavi
Plugin URI: https://lesterchan.net/portfolio/programming/php/
Description: Adds a more advanced paging navigation to your WordPress blog
Version: 2.94.1
Author: Lester 'GaMerZ' Chan
Author URI: https://lesterchan.net
Text Domain: wp-pagenavi
*/
require_once __DIR__ . '/scb/load.php';
function _pagenavi_init() {
load_plugin_textdomain( 'wp-pagenavi' );
require_once __DIR__ . '/core.php';
$options = new scbOptions( 'pagenavi_options', __FILE__, array(
'pages_text' => __( 'Page %CURRENT_PAGE% of %TOTAL_PAGES%', 'wp-pagenavi' ),
'current_text' => '%PAGE_NUMBER%',
'page_text' => '%PAGE_NUMBER%',
'first_text' => __( '&laquo; First', 'wp-pagenavi' ),
'last_text' => __( 'Last &raquo;', 'wp-pagenavi' ),
'prev_text' => __( '&laquo;', 'wp-pagenavi' ),
'next_text' => __( '&raquo;', 'wp-pagenavi' ),
'dotleft_text' => __( '...', 'wp-pagenavi' ),
'dotright_text' => __( '...', 'wp-pagenavi' ),
'num_pages' => 5,
'num_larger_page_numbers' => 3,
'larger_page_numbers_multiple' => 10,
'always_show' => false,
'use_pagenavi_css' => true,
'style' => 1,
) );
PageNavi_Core::init( $options );
if ( is_admin() ) {
require_once __DIR__ . '/admin.php';
new PageNavi_Options_Page( __FILE__, $options );
}
}
scb_init( '_pagenavi_init' );