From b5008205b5b3669f74b008533cae2fd3c512cfbf Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 27 Sep 2010 20:26:36 +0000 Subject: [PATCH] Admin bar, first pass. see #14772 git-svn-id: http://svn.automattic.com/wordpress/trunk@15671 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/admin-bar.php | 346 +++++++++++++ wp-includes/admin-bar/admin-bar-class.php | 168 +++++++ wp-includes/admin-bar/admin-bar-css.php | 453 ++++++++++++++++++ wp-includes/admin-bar/admin-bar-debug.php | 161 +++++++ .../admin-bar/admin-bar-superadmin.php | 156 ++++++ wp-includes/images/admin-bar-sprite-rtl.png | Bin 0 -> 18950 bytes wp-includes/images/admin-bar-sprite.png | Bin 0 -> 15332 bytes wp-includes/images/wpmini-blue.png | Bin 0 -> 958 bytes wp-includes/ms-functions.php | 55 --- wp-includes/user.php | 161 +++++++ wp-settings.php | 1 + 11 files changed, 1446 insertions(+), 55 deletions(-) create mode 100644 wp-includes/admin-bar.php create mode 100644 wp-includes/admin-bar/admin-bar-class.php create mode 100644 wp-includes/admin-bar/admin-bar-css.php create mode 100644 wp-includes/admin-bar/admin-bar-debug.php create mode 100644 wp-includes/admin-bar/admin-bar-superadmin.php create mode 100644 wp-includes/images/admin-bar-sprite-rtl.png create mode 100644 wp-includes/images/admin-bar-sprite.png create mode 100644 wp-includes/images/wpmini-blue.png diff --git a/wp-includes/admin-bar.php b/wp-includes/admin-bar.php new file mode 100644 index 000000000..6af0fe317 --- /dev/null +++ b/wp-includes/admin-bar.php @@ -0,0 +1,346 @@ +menu member var. + * This is called very late on the footer actions so that it will render after anything else being + * added to the footer. + * + * It includes the action "wp_before_admin_bar_render" which should be used to hook in and + * add new menus to the admin bar. That way you can be sure that you are adding at most optimal point, + * right before the admin bar is rendered. This also gives you access to the $post global, among others. + */ +function wp_admin_bar_render() { + global $wp_admin_bar; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + $wp_admin_bar->load_user_locale_translations(); + + do_action( 'wp_before_admin_bar_render' ); + + $wp_admin_bar->render(); + + do_action( 'wp_after_admin_bar_render' ); + + $wp_admin_bar->unload_user_locale_translations(); +} +add_action( 'wp_footer', 'wp_admin_bar_render', 1000 ); +add_action( 'admin_footer', 'wp_admin_bar_render', 1000 ); + +/** + * Show the logged in user's gravatar as a separator. + */ +function wp_admin_bar_me_separator() { + global $wp_admin_bar, $current_user; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + $wp_admin_bar->add_menu( array( 'id' => 'me', 'title' => get_avatar( $current_user->ID, 16 ), 'href' => $wp_admin_bar->user->account_domain . 'wp-admin/profile.php' ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_me_separator', 10 ); + +/** + * Use the $wp_admin_bar global to add the "My Account" menu and all submenus. + */ +function wp_admin_bar_my_account_menu() { + global $wp_admin_bar, $current_user; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + /* Add the 'My Account' menu */ + $wp_admin_bar->add_menu( array( 'id' => 'my-account', 'title' => __( 'My Account' ), 'href' => admin_url('profile.php') ) ); + + /* Add the "My Account" sub menus */ + $wp_admin_bar->add_menu( array( 'parent' => 'my-account', 'title' => __( 'Edit My Profile' ), 'href' => admin_url('profile.php') ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'my-account', 'title' => __( 'Global Dashboard' ), 'href' => admin_url() ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'my-account', 'title' => __( 'Log Out' ), 'href' => wp_logout_url() ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_my_account_menu', 20 ); + +/** + * Use the $wp_admin_bar global to add the "My Blogs/[Blog Name]" menu and all submenus. + */ +function wp_admin_bar_my_blogs_menu() { + global $wpdb, $wp_admin_bar; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + /* Remove the global dashboard */ + if ( is_multisite() ) { + foreach ( (array) $wp_admin_bar->user->blogs as $key => $blog ) { + if ( get_dashboard_blog() == $blog->domain ) + unset( $wp_admin_bar->user->blogs[$key] ); + } + } + + /* Add the 'My Dashboards' menu if the user has more than one blog. */ + if ( count( $wp_admin_bar->user->blogs ) > 1 ) { + $wp_admin_bar->add_menu( array( 'id' => 'my-blogs', 'title' => __( 'My Blogs' ), 'href' => $wp_admin_bar->user->account_domain ) ); + + $default = includes_url('images/wpmini-blue.png'); + + $counter = 2; + foreach ( (array) $wp_admin_bar->user->blogs as $blog ) { + $blogdomain = preg_replace( '!^https?://!', '', $blog->siteurl ); + // @todo Replace with some favicon lookup. + //$blavatar = 'Blavatar'; + $blavatar = 'Blavatar';; + + $marker = ''; + if ( strlen($blog->blogname) > 35 ) + $marker = '...'; + + if ( empty( $blog->blogname ) ) + $blogname = $blog->domain; + else + $blogname = substr( $blog->blogname, 0, 35 ) . $marker; + + if ( !isset( $blog->visible ) || $blog->visible === true ) { + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-' . $blog->userblog_id, 'title' => $blavatar . $blogname, 'href' => constant( 'PROTO' ) . $blogdomain . '/wp-admin/' ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-d', 'title' => __( 'Dashboard' ), 'href' => constant( 'PROTO' ) . $blogdomain . '/wp-admin/' ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-n', 'title' => __( 'New Post' ), 'href' => constant( 'PROTO' ) . $blogdomain . '/wp-admin/post-new.php' ) ); + // @todo, stats plugins should add this: + //$wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-s', 'title' => __( 'Blog Stats' ), 'href' => constant( 'PROTO' ) . $blogdomain . '/wp-admin/index.php?page=stats' ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-c', 'title' => __( 'Manage Comments' ), 'href' => constant( 'PROTO' ) . $blogdomain . '/wp-admin/edit-comments.php' ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'blog-' . $blog->userblog_id, 'id' => 'blog-' . $blog->userblog_id . '-v', 'title' => __( 'Read Blog' ), 'href' => constant( 'PROTO' ) . $blogdomain ) ); + } + $counter++; + } + + /* Add the "Manage Blogs" menu item */ + // @todo, use dashboard blog. + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'manage-blogs', 'title' => __( 'Manage Blogs' ), admin_url('my-sites.php') ) ); + + /* Add the 'My Dashboard' menu if the user only has one blog. */ + } else { + $wp_admin_bar->add_menu( array( 'id' => 'my-blogs', 'title' => __( 'My Blog' ), 'href' => $wp_admin_bar->user->account_domain ) ); + + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-1-d', 'title' => __( 'Dashboard' ), 'href' => admin_url() ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-1-n', 'title' => __( 'New Post' ), 'href' => admin_url('post-new.php') ) ); + // @todo Stats plugins should add this. + //$wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-1-s', 'title' => __( 'Blog Stats' ), 'href' => admin_ur;('index.php?page=stats') ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-1-c', 'title' => __( 'Manage Comments' ), 'href' => admin_url('edit-comments.php') ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'my-blogs', 'id' => 'blog-1-v', 'title' => __( 'Read Blog' ), 'href' => home_url() ) ); + } +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_my_blogs_menu', 30 ); + +/** + * Show the blavatar of the current blog as a separator. + */ +function wp_admin_bar_blog_separator() { + global $wp_admin_bar, $current_user, $current_blog; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + $default = includes_url('images/wpmini-blue.png'); + + $wp_admin_bar->add_menu( array( 'id' => 'blog', 'title' => '' . __( 'Current blog avatar' ) . '', 'href' => home_url() ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_blog_separator', 40 ); + +/** + * Use the $wp_admin_bar global to add a menu for blog info, accessable to all users. + */ +function wp_admin_bar_bloginfo_menu() { + global $wp_admin_bar; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + /* Add the Blog Info menu */ + $wp_admin_bar->add_menu( array( 'id' => 'bloginfo', 'title' => __( 'Blog Info' ), 'href' => '' ) ); + + $wp_admin_bar->add_menu( array( 'parent' => 'bloginfo', 'title' => __( 'Get Shortlink' ), 'href' => '', 'meta' => array( 'onclick' => 'javascript:function wpcomshort() { var url=document.location;var links=document.getElementsByTagName('link');var found=0;for(var i = 0, l; l = links[i]; i++){if(l.getAttribute('rel')=='shortlink') {found=l.getAttribute('href');break;}}if (!found) {for (var i = 0; l = document.links[i]; i++) {if (l.getAttribute('rel') == 'shortlink') {found = l.getAttribute('href');break;}}}if (found) {prompt('URL:', found);} else {alert('No shortlink available for this page'); } return false; } wpcomshort();' ) ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_bloginfo_menu', 50 ); + +/** + * Use the $wp_admin_bar global to add the "Edit Post" menu when viewing a single post. + */ +function wp_admin_bar_edit_menu() { + global $post, $wp_admin_bar; + + if ( !is_object( $wp_admin_bar ) ) + return false; + + if ( !is_single() && !is_page() ) + return false; + + if ( !$post_type_object = get_post_type_object( $post->post_type ) ) + return false; + + if ( !current_user_can( $post_type_object->cap->edit_post, $post->ID ) ) + return false; + + $wp_admin_bar->add_menu( array( 'id' => 'edit', 'title' => __( 'Edit' ), 'href' => get_edit_post_link( $post->ID ) ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_edit_menu', 100 ); + +/** + * Load up the CSS needed to render the admin bar nice and pretty. + */ +function wp_admin_bar_css() { + global $pagenow, $wp_locale; + + if ( !is_user_logged_in() ) + return false; + + if ( 'press-this.php' == $pagenow ) + return; + + $nobump = false; + + /* Wish we could use wp_enqueue_style() here, but it will not let us pass GET params to the stylesheet correctly. */ + ?> + + + + diff --git a/wp-includes/admin-bar/admin-bar-class.php b/wp-includes/admin-bar/admin-bar-class.php new file mode 100644 index 000000000..91e30dd5f --- /dev/null +++ b/wp-includes/admin-bar/admin-bar-class.php @@ -0,0 +1,168 @@ +user = new stdClass; + $this->menu = new stdClass; + + /* Populate settings we need for the menu based on the current user. */ + $this->user->blogs = get_ordered_blogs_of_user( $current_user->id ); + if ( is_multisite() ) { + $this->user->active_blog = get_active_blog_for_user( $current_user->id ); + $this->user->domain = ( $this->user->active_blog == 'username only' ) ? get_dashboard_blog() : trailingslashit( get_home_url( $this->user->active_blog->blog_id ) ); + $this->user->account_domain = $this->user->domain; + } else { + $this->user->active_blog = $this->user->blogs[$blog_id]; + $this->user->domain = home_url(); + $this->user->account_domain = home_url(); + } + $this->user->locale = get_locale(); + } + + function add_menu( $args = array() ) { + $defaults = array( + 'title' => false, + 'href' => false, + 'parent' => false, // false for a root menu, pass the ID value for a submenu of that menu. + 'id' => false, // defaults to a sanitized title value. + 'meta' => false // array of any of the following options: array( 'html' => '', 'class' => '', 'onclick' => '', target => '' ); + ); + + $r = wp_parse_args( $args, $defaults ); + extract( $r, EXTR_SKIP ); + + if ( empty( $title ) ) + return false; + + /* Make sure we have a valid ID */ + if ( empty( $id ) ) + $id = esc_attr( sanitize_title( trim( $title ) ) ); + + if ( !empty( $parent ) ) { + /* Add the menu to the parent item */ + $child = array( + 'id' => $id, + 'title' => $title, + 'href' => $href + ); + + if ( !empty( $meta ) ) + $child['meta'] = $meta; + + $this->add_node( $parent, $this->menu, $child ); + } else { + /* Add the menu item */ + $this->menu->{$id} = array( + 'title' => $title, + 'href' => $href + ); + + if ( !empty( $meta ) ) + $this->menu->{$id}['meta'] = $meta; + } + } + + function remove_menu( $id ) { + return $this->remove_node( $id, $this->menu ); + } + + function render() { + ?> +
+ + +
+
+ +
+
+
+ + menu = null; + } + + /* Helpers */ + function recursive_render( $id, &$menu_item ) { ?> + + +
  • "> + onclick=""target=""> + + + + + + + + +
  • &$menu_item ) { + if ( $parent_id == $id ) { + $menu->{$parent_id}['children']->{$child['id']} = $child; + $child = null; + return true; + } + + if ( !empty( $menu->{$id}['children'] ) ) + $this->add_node( $parent_id, $menu->{$id}['children'], $child ); + } + $child = null; + + return false; + } + + function remove_node( $id, &$menu ) { + foreach( $menu as $menu_item_id => &$menu_item ) { + if ( $menu_item_id == $id ) { + $menu_item = null; + return true; + } + + if ( !empty( $menu->{$menu_item_id}['children'] ) ) + $this->remove_node( $id, $menu->{$menu_item_id}['children'] ); + } + + return false; + } + + function load_user_locale_translations() { + $this->need_to_change_locale = ( get_locale() != $this->user->locale ); + if ( !$this->need_to_change_locale ) return; + $this->previous_translations = get_translations_for_domain( 'default' ); + $this->adminbar_locale_filter = lambda( '$_', '$GLOBALS["wp_admin_bar"]->user->locale;' ); + unload_textdomain( 'default' ); + add_filter( 'locale', $this->adminbar_locale_filter ); + load_default_textdomain(); + $this->changed_locale = true; + } + + function unload_user_locale_translations() { + global $l10n; + if ( !$this->changed_locale ) return; + remove_filter( 'locale', $this->adminbar_locale_filter ); + $l10n['default'] = &$this->previous_translations; + + } +} +?> diff --git a/wp-includes/admin-bar/admin-bar-css.php b/wp-includes/admin-bar/admin-bar-css.php new file mode 100644 index 000000000..64513fecf --- /dev/null +++ b/wp-includes/admin-bar/admin-bar-css.php @@ -0,0 +1,453 @@ + + +#wpadminbar { direction:ltr; background:#666 url() 0 -222px repeat-x; color:#ddd; font:12px Arial, Helvetica, sans-serif; height:28px; left:0; margin:0; position:fixed; top:0; width:100%; z-index:99999; min-width: 960px; } +#wpadminbar ul, #wpadminbar ul li { position: relative; z-index: 99999; } +#wpadminbar ul li img { vertical-align: middle !important; margin-right: 8px !important; border: none !important; padding: 0 !important; } +#wpadminbar .quicklinks > ul > li > a { border-right: 1px solid #686868; border-left: 1px solid #808080; } +#wpadminbar .quicklinks > ul > li.ab-subscriptions > a, #wpadminbar .quicklinks > ul > li:last-child > a { border-right: none; } +#wpadminbar .quicklinks > ul > li.hover > a { border-left-color: #707070; } +#wpadminbar a { outline: none; } +#wpadminbar .avatar {border:1px solid #999 !important;padding:0 !important;margin:-3px 5px 0 0 !important;vertical-align:middle;float:none;display:inline !important; } +#wpadminbar .menupop ul li a {color:#555 !important;text-shadow:none;font-weight:normal;white-space:nowrap;} +#wpadminbar .menupop a > span {background:url() 100% 100.4% no-repeat;padding-right:.8em;line-height: 28px;} +#wpadminbar .menupop ul li a > span { display: block; background:url() 100% 97.2% no-repeat;padding-right:1.5em;line-height: 28px;} +#wpadminbar .menupop ul li a span#awaiting-mod { display: inline; background: #aaa; color: #fff; padding: 1px 5px; font-size: 10px; font-family: verdana; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } +#wpadminbar .menupop ul li a:hover span#awaiting-mod { background: #fff; color: #888; } +#wpadminbar .menupop ul {-moz-box-shadow:0 4px 8px rgba(0,0,0,0.1);-webkit-box-shadow:0 4px 8px rgba(0,0,0,0.1);background:#fff;display:none;position:absolute;border:1px solid #dfdfdf;border-top:none !important;float:none} +html>body #wpadminbar .menupop ul {background:rgba(255,255,255,0.97);border-color:rgba(0,0,0,0.1);} +#wpadminbar .menupop.ab-my-account ul, #wpadminbar .menupop.ab-my-dash ul, #wpadminbar .menupop.ab-new-post ul {min-width:140px} +#wpadminbar .menupop li {float:none;margin:0;padding:0;background-image:none;} +#wpadminbar .quicklinks a {border:none;color:#ddd !important;text-shadow:#555 0px -1px 0px;display:block;font:13px Arial, Helvetica, sans-serif;font-weight:normal;letter-spacing:normal;padding:0 0.85em;line-height:28px;text-decoration:none !important;} +#wpadminbar .quicklinks a:hover {text-shadow:#333 0px -1px 0px;} +#wpadminbar li.ab-privacy { float: right; background: #333; } +#wpadminbar li.ab-privacy > a > span { background: none; padding: 0; } +#wpadminbar li.ab-privacy span#priv-icon { display: block; text-indent: -999em; background:url() 40% 59.7% no-repeat; padding: 0; width: 13px; margin-right: -3px; } + +#wpadminbar li.ab-sadmin { float: right; background: #555 } +#wpadminbar li.ab-sadmin ul, #wpadminbar li.ab-privacy ul { right: 0; float: right; } +#wpadminbar li.ab-sadmin > a { font-size: 11px !important; padding: 0 7px !important; border: none !important; border-left: 1px solid #666 !important; } + +#wpadminbar li.ab-sadmin ul a, #wpadminbar li.ab-privacy a { border-right: none !important; border-left: none !important; } +#wpadminbar li.ab-sadmin ul li { right: 0; float: right; text-align: left; width: 100%; } +#wpadminbar li.ab-sadmin ul li a { padding-left: 1.75em; } +#wpadminbar li.ab-sadmin ul li a > span { background:url() 0% 101.8% no-repeat;padding-left: 1.25em; margin-left: -1.25em; line-height: 28px; padding-right: 0 !important; } +#wpadminbar li a.loading { background: url() 32% 59.8% no-repeat !important; text-indent: -999em; overflow: hidden; padding: 0 16px 0 0; height: 28px; display: block; float: left; margin-right: 2px; } + +#wpadminbar li:hover {background: #555 url() 0 -282px repeat-x;} +#wpadminbar li li:hover { color:#fff !important; background: #888 url() 0 -222px repeat-x !important;text-shadow: #666 0px -1px 0px;} +#wpadminbar li li:hover > a { color:#fff !important; } +.quicklinks ul {list-style:none;margin:0;padding:0;text-align:left} +.quicklinks ul li {float:left;margin:0} + +#adminbarlogin {float:left;display:inline;} + +#adminbarsearch {float:right; } +#adminbarsearch {height: 18px;padding: 3px;} +#adminbarsearch * {color: #555;font-size:12px;} +#adminbarsearch label, #adminbarsearch a { height: 28px; color: #ccc; display:block;float:left;padding:3px 4px;text-shadow:0px -1px 0px #444;} +#adminbarsearch a {text-decoration:underline;} +#adminbarsearch a:hover {color:#fff;} + +#wpadminbar li.ab-me:hover, #wpadminbar li.ab-blog:hover { background:none;} +#wpadminbar li.ab-me > a, #wpadminbar li.ab-blog > a { line-height: 18px !important; border: none !important; background:url() 100% 59.8% no-repeat; height: 28px; padding: 0 1.15em 0 0.7em; } +#wpadminbar li.ab-me > a.hover, #wpadminbar li.ab-blog > a.hover { background-position: 67% 59.8%; } +#wpadminbar li.ab-me img.avatar, #wpadminbar li.ab-blog img.avatar { margin: 4px 0 0 0 !important; vertical-align: middle; background: #eee; width: 16px !important; height: 16px !important; } +#wpadminbar li.ab-my-account a, #wpadminbar li.ab-bloginfo a { border-left: none !important; padding-left: 0.7em !important; margin-top: 0 !important; } +#wpadminbar li.ab-my-account > ul, #wpadminbar li.ab-bloginfo > ul { left: -7px; } +#wpadminbar ul li img { width: 16px !important; height: 16px !important; } + +#wpadminbar ul li a strong.count { text-shadow: none; background: #ddd; color: #555; margin-left: 5px; padding: 1px 6px; top: -1px; position: relative; font-size: 9px; -moz-border-radius: 7px; -webkit-border-radius: 7px; border-radius: 7px; font-weight: normal } + +#wpadminbar #q { + line-height:normal !important; + width:140px !important; + margin-top:0px !important; +} +.adminbar-input { + display:block !important; + float:left !important; + font:12px Arial,Helvetica,sans-serif !important; + border:1px solid #626262 !important; + padding:2px 3px !important; + margin-right:3px !important; + background:#ddd url() top left no-repeat !important; + -webkit-border-radius:0 !important; + -khtml-border-radius:0 !important; + -moz-border-radius:0 !important; + border-radius:0 !important; + outline:none; + text-shadow:0 1px 0 #fff; +} +button.adminbar-button { + position:relative; + border:0; + cursor:pointer; + overflow:visible; + margin:0 !important; + float:left; + background:url() right -107px no-repeat; + padding:0 14px 0 0; + text-align:center; +} +button.adminbar-button span { + position:relative; + display:block; + white-space:nowrap; + height:19px; + background:url() left -69px no-repeat; + padding:3px 0 0 14px; + font:12px Arial,Helvetica,sans-serif !important; + font-weight:bold !important; + color:#444 !important; + text-shadow:0px 1px 0px #eee !important; +} +button.adminbar-button:active { + background-position:right -184px !important; + text-shadow:0px 1px 0px #eee !important; +} +button.adminbar-button:hover span { + color:#000 !important; +} +button.adminbar-button:active span { + background-position:left -146px !important; +} +button.adminbar-button::-moz-focus-inner { + border:none; +} +@media screen and (-webkit-min-device-pixel-ratio:0) { + button.adminbar-button span { + margin-top: -1px; + } +} + + + #wpadminbar { + direction:rtl; + font-family: Tahoma, Arial ,sans-serif; + right:0; + left:auto; + } + #wpadminbar div, #wpadminbar ul, #wpadminbar ul li { + min-height: 0; + } + #wpadminbar ul li img { margin-left: 8px !important; margin-right: 0 !important; } + #wpadminbar .quicklinks > ul > li > a { border-left: 1px solid #686868; border-right: 1px solid #808080;} + #wpadminbar .quicklinks > ul > li.ab-subscriptions > a, #wpadminbar .quicklinks > ul > li:last-child > a { border-left: none; border-right: 1px solid #808080;} + #wpadminbar .quicklinks > ul > li.hover > a { border-right-color: #707070; border-left-color: #686868; } + #wpadminbar .avatar {margin: -3px 0 0 5px !important; float:none; } + #wpadminbar .menupop a > span {background-position: 0 100.4%; padding-left:.8em;} + #wpadminbar .menupop ul li a > span { background-position: 0% 97.2%; padding-right:0;padding-left:1.5em } + #wpadminbar .menupop ul {right: 0; width:100%; min-width:150px;} + #wpadminbar .ab-my-account ul { width:200px;} + #wpadminbar .ab-my-blogs ul { width:300px;} + #wpadminbar .ab-my-blogs ul ul { width:200px;} + #wpadminbar .ab-subscribe ul { width:150px;} + #wpadminbar .ab-bloginfo ul { width:200px;} + #wpadminbar .ab-subscribe ul { width:150px;} + #wpadminbar .ab-subscriptions ul { width:200px;} + #wpadminbar .menupop ul li {width:auto} + #wpadminbar .quicklinks a {font-family: Tahoma, Arial, Helvetica, sans-serif;} + #wpadminbar li.ab-privacy { float: left; } + #wpadminbar li.ab-privacy span#priv-icon { text-indent: 999em; background-position: 60% 59.7%; padding: 0; margin-right: 0; margin-left: -3px;} + + #wpadminbar li.ab-sadmin { float: left; } + #wpadminbar li.ab-sadmin ul, #wpadminbar li.ab-privacy ul { right: auto; left: 0; float: left; } + #wpadminbar li.ab-sadmin > a { border-right: 1px solid #666 !important; border-left:none !important;} + + #wpadminbar li.ab-sadmin ul a, #wpadminbar li.ab-privacy a { border-right: none !important; border-left: none !important; } + #wpadminbar li.ab-sadmin ul li { left: 0; right:auto; float: left; text-align: right; } + + + #wpadminbar li.ab-sadmin ul li a { padding-right: 1.75em; padding-left: 0 } + #wpadminbar li.ab-sadmin ul li a > span { background-position: 100% 101.8%; padding-right: 1.25em !important; padding-left: 0 !important; margin-right: -1.25em; margin-left: 0; } + #wpadminbar li a.loading { background-position: right 50% !important; padding-right: 29px; padding-left: 0;} + #wpadminbar li.subscribed a strong { background-position: 68% 59.8% !important; padding: 0 0 0 16px; float: right; margin-left: 2px; } + + + .quicklinks ul {text-align:right} + .quicklinks ul li {float:right;} + + #adminbarlogin {float:right;} + + #adminbarsearch {display:none;} + #adminbarsearch label, #adminbarsearch a { float:right;} + + #wpadminbar li.ab-me > a, #wpadminbar li.ab-blog > a { background-position:0% 59.8%; padding: 0 0.7em 0 1.15em; } + #wpadminbar li.ab-me > a.hover, #wpadminbar li.ab-blog > a.hover { background-position: 33% 59.8%; } + #wpadminbar li.ab-my-account a, #wpadminbar li.ab-bloginfo a { border-right: none !important; padding-right: 0.7em !important; } + #wpadminbar li.ab-my-account > ul, #wpadminbar li.ab-bloginfo > ul { right: -7px; left:auto;} + + #wpadminbar ul li a strong.count { margin-right: 5px; margin-left: 0; position:static} + + + .adminbar-input { + float:right !important; + font-family: Tahoma, Arial,Helvetica,sans-serif !important; + margin-right:3px !important; + margin-left:0 !important; + background-position: right top !important; + } + button.adminbar-button { + float:right; + background-position: left -107px; + padding:0 0 0 14px; + } + button.adminbar-button span { + background-position: right -69px; + padding:3px 14px 0 0; + font-family: Tahoma, Arial,Helvetica,sans-serif !important; + } + button.adminbar-button:active { + background-position:left -184px !important; + } + button.adminbar-button:active span { + background-position:right -146px !important; + } + + body { padding-top: 28px !important; } + + + + body { padding-top: 28px; background-position: 0px 28px; } + + + + #querylist { + font-family: Arial, Tahoma, sans-serif; + display: none; + position: absolute; + top: 50px; + left: 50px; + right: 50px; + background: #fff; + padding: 20px; + -moz-box-shadow: 0 0 15px #888; + -webkit-box-shadow: 0 0 15px #888; + box-shadow: 0 0 15px #888; + z-index: 99999; + border: 10px solid #f0f0f0; + color: #000; + line-height: 150% !important; + } + #querylist pre { + font-size: 12px; + padding: 10px; + } + + #querylist h1 { + font-family: georgia, times, serif; + text-align: center; + font-size: 24px; + padding: 20px 5px; + background: #eee; + color: #555; + margin: 0; + } + #querylist div#debug-status { + background: #ccc; + color: #fff; + overflow: hidden; + height: 21px; + font-size: 14px; + font-family: georgia, times, serif; + padding: 7px 15px; + } + #querylist .left { float: left; } + #querylist .right { float: right; } + + #querylist h1, #querylist h2, #querylist h3 { + font-weight: normal; + } + + #querylist ul.debug-menu-links { + clear: left; + background: #ccc; + padding: 10px 15px 0; + overflow: hidden; + list-style: none; + margin: 0; + padding: 0 0 0 15px; + } + #querylist ul.debug-menu-links li { + float: left; + margin-right: 10px; + margin-bottom: 0 !important; + } + + #querylist ul.debug-menu-links li a { + outline: none; + display: block; + padding: 5px 9px; + margin-right: 0; + background: #bbb; + color: #fff !important; + text-decoration: none !important; + font-weight: normal; + font-size: 12px; + color: #555; + -webkit-border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; + } + #querylist ul.debug-menu-links li.current a { + background: #fff; + color: #555 !important; + } + + #querylist h2 { + float: left; + min-width: 150px; + border: 1px solid #eee; + padding: 5px 10px 15px; + clear: none; important; + text-align: center; + font-family: georgia, times, serif; + font-size: 28px; + margin: 15px 10px 15px 0 !important; + } + #querylist h2 span { + font-size: 12px; + color: #888; + text-transform: uppercase; + white-space: nowrap; + display: block; + margin-bottom: 5px; + } + + #object-cache-stats h2 { + border: none; + float: none; + text-align: left; + font-size: 22px; + margin-bottom: 0; + } + + #object-cache-stats ul.debug-menu-links { + padding: 0; + margin: 0; + background: none; + } + #object-cache-stats ul.debug-menu-links li { + float: left; + margin-bottom: 10px !important; + background: none !important; + border: 1px solid #eee !important; + color: #555 !important; + } + #object-cache-stats ul.debug-menu-links li.current a { + background: #ccc !important; + color: #fff !important; + -webkit-border-top-right-radius: 0; + -webkit-border-top-left-radius: 0; + -moz-border-radius-topright: 0; + -moz-border-radius-topleft: 0; + } + + #object-cache-stats ul.debug-menu-links li a { + background: none; + color: #555 !important; + overflow: hidden; + } + + #querylist h3 { + margin-bottom: 15px; + } + + #querylist ol#wpd-queries { + padding: 0 !important; + margin: 0 !important; + list-style: none; + clear: left; + } + #querylist ol#wpd-queries li { + padding: 10px; + background: #f0f0f0; + margin: 0 0 10px 0; + } + #querylist ol#wpd-queries li div.qdebug { + background: #e8e8e8; + margin: 10px -10px -10px -10px; + padding: 5px 150px 5px 5px; + font-size: 11px; + position: relative; + min-height: 20px; + } + + #querylist ol#wpd-queries li div.qdebug span { + position: absolute; + right: 10px; + top: 5px; + white-space: nowrap; + } + + #querylist a { + text-decoration: underline !important; + color: blue !important; + } + #querylist a:hover { + text-decoration: none !important; + } + #querylist .debug-menu-target { + display: none; + } + + #querylist ol { + font: 12px Monaco, "Courier New", Courier, Fixed !important; + line-height: 180% !important; + } + + #wpadminbar #admin-bar-micro ul li { + list-style-type: none; + position: relative; + margin: 0; + padding: 0; + } + #wpadminbar #admin-bar-micro ul ul, #wpadminbar #admin-bar-micro #awaiting-mod, #wpadminbar .ab-sadmin .count-0 { + display: none !important; + } + #wpadminbar #admin-bar-micro ul li:hover > ul { + display: block; + position: absolute; + top: -1px; + left: 100%; + } + #wpadminbar #admin-bar-micro li a { + display: block; + text-decoration: none; + } + #wpadminbar #admin-bar-micro li li a { + background: #ddd; + } + #wpadminbar #admin-bar-micro li li li a { + background: #fff; + } + + + + #querylist { + direction: ltr; + } + + #wpadminbar #admin-bar-micro ul li:hover > ul { + left: auto; + right: 100%; + } + + \ No newline at end of file diff --git a/wp-includes/admin-bar/admin-bar-debug.php b/wp-includes/admin-bar/admin-bar-debug.php new file mode 100644 index 000000000..5d1c0df20 --- /dev/null +++ b/wp-includes/admin-bar/admin-bar-debug.php @@ -0,0 +1,161 @@ +num_queries; + $seconds = timer_stop(); + + /* Add the main siteadmin menu item */ + $wp_admin_bar->add_menu( array( 'id' => 'queries', 'title' => "{$queries}q/{$seconds}", 'href' => 'javascript:toggle_query_list()', 'meta' => array( 'class' => 'ab-sadmin' ) ) ); +} +add_action( 'wp_before_admin_bar_render', 'wp_admin_bar_debug_menu', 1000 ); + +function wp_admin_bar_query_debug_list() { + global $wpdb, $wp_object_cache; + + if ( !is_super_admin() ) + return false; + + $debugs = array(); + + if ( defined('SAVEQUERIES') && SAVEQUERIES ) + $debugs['wpdb'] = array( __('Queries'), 'wp_admin_bar_debug_queries' ); + + if ( is_object($wp_object_cache) && method_exists($wp_object_cache, 'stats') ) + $debugs['object-cache'] = array( __('Object Cache'), 'wp_admin_bar_debug_object_cache' ); + + $debugs = apply_filters( 'wp_admin_bar_debugs_list', $debugs ); + + if ( empty($debugs) ) + return; + +?> + +
    + +

    Debugging blog # on

    +
    +

    +

    PHP Version:

    +
    + + +
    + + $debug_output ) : ?> + +
    > + +
    + + + +
    + + + +
    + +queries) ) { + $show_many = isset($_GET['debug_queries']); + + if ( $wpdb->num_queries > 500 && !$show_many ) + $out .= "

    There are too many queries to show easily! Show them anyway.

    "; + + $out .= '
      '; + $first_query = 0; + $counter = 0; + + foreach ( $wpdb->queries as $q ) { + list($query, $elapsed, $debug) = $q; + + $total_time += $elapsed; + + if ( !$show_many && ++$counter > 500 ) + continue; + + $query = nl2br(esc_html($query)); + + // $dbhname, $host, $port, $name, $tcp, $elapsed + $out .= "
    1. $query
      $debug #{$counter} (" . number_format(sprintf('%0.1f', $elapsed * 1000), 1, '.', ',') . "ms)
    2. \n"; + } + $out .= '
    '; + } else { + $out .= "

    There are no queries on this page, you won the prize!!! :)

    "; + } + + $query_count = '

    Total Queries:' . number_format( $wpdb->num_queries ) . "

    \n"; + $query_time = '

    Total query time:' . number_format(sprintf('%0.1f', $total_time * 1000), 1) . "ms

    \n"; + $memory_usage = '

    Peak Memory Used:' . number_format( memory_get_peak_usage( ) ) . " bytes

    \n"; + + $out = $query_count . $query_time . $memory_usage . $out; + + return $out; +} + +function wp_admin_bar_debug_object_cache() { + global $wp_object_cache; + ob_start(); + echo "
    "; + $wp_object_cache->stats(); + echo "
    "; + $out = ob_get_contents(); + ob_end_clean(); + + return $out; +} + +?> \ No newline at end of file diff --git a/wp-includes/admin-bar/admin-bar-superadmin.php b/wp-includes/admin-bar/admin-bar-superadmin.php new file mode 100644 index 000000000..c93e6a344 --- /dev/null +++ b/wp-includes/admin-bar/admin-bar-superadmin.php @@ -0,0 +1,156 @@ +add_menu( array( 'id' => 'superadmin', 'title' => 'μ', 'href' => '', 'meta' => array( 'class' => 'ab-sadmin' ) ) ); + + wp_admin_bar_build_snackmenu(); + + /* Get the settings we need for the current blog */ + $matureaction = $current_blog->mature ? 'unmatureblog' : 'matureblog'; + $maturetext = $current_blog->mature ? esc_attr__('Unmark as mature') : esc_attr__('Mark as mature'); + $suspendtext = $current_blog->spam ? esc_attr('Unsuspend blog') : esc_attr('Suspend blog'); + $suspendaction = $current_blog->spam ? 'unspamblog' : 'spamblog'; + $mature_url = admin_url( "ms-edit.php?action=confirm&action2={$matureaction}&id={$current_blog->blog_id}&msg=" . urlencode( 'Are you sure you want to ' . strtolower( $maturetext ) . " {$current_blog->domain} as mature?" ) ); + $suspend_url = admin_url( "ms-edit.php?action=confirm&action2={$suspendaction}&id={$current_blog->blog_id}&msg=" . urlencode( 'Are you sure you want to ' . strtolower( $suspendtext ) . " {$current_blog->domain} ?" ) ); + + /* Add the submenu items to the Super Admin menu */ + $wp_admin_bar->add_menu( array( 'parent' => 'superadmin', 'title' => __( 'Blog Dashboard' ), 'href' => admin_url(), 'position' => 10 ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'superadmin', 'title' => __( 'Blog Options' ), 'href' => admin_url( "ms-sites.php?action=blogs&searchaction=id&s={$current_blog->blog_id}" ), 'position' => 30 ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'superadmin', 'title' => "$maturetext", 'href' => $mature_url, 'position' => 50 ) ); + $wp_admin_bar->add_menu( array( 'parent' => 'superadmin', 'title' => "$suspendtext", 'href' => $suspend_url, 'position' => 80 ) ); +} + +function wp_admin_bar_build_snackmenu() { + global $wp_admin_bar, $menu, $submenu, $pagenow; + + if ( !is_object( $wp_admin_bar ) || !is_super_admin() ) + return false; + + // Hide moderation count, filter removed at the bottom of this function + add_filter( 'wp_count_comments', 'wp_admin_bar_removemodcount' ); + + require_once( ABSPATH . 'wp-admin/includes/admin.php' ); + + // menu.php assumes it is in the global scope and relies on the $wp_taxonomies global array + $wp_taxonomies = array(); + require_once( ABSPATH . 'wp-admin/menu.php' ); + + /* Add the snack menu submenu to the superadmin menu */ + $wp_admin_bar->add_menu( array( 'parent' => 'superadmin', 'title' => 'Snack Menu', 'href' => '/wp-admin/' ) ); + + /* Loop through the submenus and add them */ + foreach ( (array) $menu as $key => $item ) { + $admin_is_parent = false; + $submenu_as_parent = false; + + if ( $submenu_as_parent && !empty($submenu[$item[2]]) ) { + $submenu[$item[2]] = array_values($submenu[$item[2]]); // Re-index. + $menu_hook = get_plugin_page_hook($submenu[$item[2]][0][2], $item[2]); + $menu_file = $submenu[$item[2]][0][2]; + + if ( false !== $pos = strpos($menu_file, '?') ) + $menu_file = substr($menu_file, 0, $pos); + if ( ( ('index.php' != $submenu[$item[2]][0][2]) && file_exists(WP_PLUGIN_DIR . "/$menu_file") ) || !empty($menu_hook)) { + $admin_is_parent = true; + $wp_admin_bar->add_menu( array( 'parent' => 'snack-menu', 'title' => $item[0], 'href' => admin_url("admin.php?page={$submenu[$item[2]][0][2]}") ) ); + } else { + $wp_admin_bar->add_menu( array( 'parent' => 'snack-menu', 'title' => $item[0], 'href' => admin_url("{$submenu[$item[2]][0][2]}") ) ); + } + } else if ( current_user_can($item[1]) ) { + $menu_hook = get_plugin_page_hook($item[2], 'admin.php'); + $menu_file = $item[2]; + + if ( false !== $pos = strpos($menu_file, '?') ) + $menu_file = substr($menu_file, 0, $pos); + if ( ('index.php' != $item[2]) && file_exists(WP_PLUGIN_DIR . "/$menu_file") || !empty($menu_hook) ) { + $admin_is_parent = true; + $wp_admin_bar->add_menu( array( 'parent' => 'snack-menu', 'title' => $item[0], 'href' => admin_url("admin.php?page={$item[2]}") ) ); + } else { + $wp_admin_bar->add_menu( array( 'parent' => 'snack-menu', 'title' => $item[0], 'href' => admin_url("{$item[2]}") ) ); + } + } + + if ( !empty($submenu[$item[2]]) ) { + $first = true; + $unique_submenu = array(); + + foreach ( $submenu[$item[2]] as $sub_key => $sub_item ) { + if ( !current_user_can($sub_item[1]) || in_array( $sub_item[0], $unique_submenu ) ) + continue; + + $unique_submenu[] = $sub_item[0]; + + if ( $first ) + $first = false; + + $menu_file = $item[2]; + if ( false !== $pos = strpos($menu_file, '?') ) + $menu_file = substr($menu_file, 0, $pos); + + $menu_hook = get_plugin_page_hook($sub_item[2], $item[2]); + $sub_file = $sub_item[2]; + if ( false !== $pos = strpos($sub_file, '?') ) + $sub_file = substr($sub_file, 0, $pos); + + if ( ( ('index.php' != $sub_item[2]) && file_exists(WP_PLUGIN_DIR . "/$sub_file") ) || ! empty($menu_hook) ) { + // If admin.php is the current page or if the parent exists as a file in the plugins or admin dir + + $parent_exists = (!$admin_is_parent && file_exists(WP_PLUGIN_DIR . "/$menu_file") && !is_dir(WP_PLUGIN_DIR . "/{$item[2]}") ) || file_exists($menu_file); + if ( $parent_exists ) + $wp_admin_bar->add_menu( array( 'parent' => sanitize_title( $item[0] ), 'title' => $sub_item[0], 'href' => admin_url("{$item[2]}?page={$sub_item[2]}") ) ); + elseif ( 'admin.php' == $pagenow || !$parent_exists ) + $wp_admin_bar->add_menu( array( 'parent' => sanitize_title( $item[0] ), 'title' => $sub_item[0], 'href' => admin_url("admin.php?page={$sub_item[2]}") ) ); + else + $wp_admin_bar->add_menu( array( 'parent' => sanitize_title( $item[0] ), 'title' => $sub_item[0], 'href' => admin_url("{$item[2]}?page={$sub_item[2]}") ) ); + } else { + $wp_admin_bar->add_menu( array( 'parent' => sanitize_title( $item[0] ), 'title' => $sub_item[0], 'href' => admin_url("{$sub_item[2]}") ) ); + } + } + } + } + + remove_filter( 'wp_count_comments', 'wp_admin_bar_removemodcount' ); +} + +// Short circuits wp_count_comments() for the front end +function wp_admin_bar_removemodcount( $stats ) { + if ( is_admin() ) + return $stats; + + $stats = array( + 'moderated' => 0, + 'approved' => 0, + 'spam' => 0, + 'trash' => 0, + 'post-trashed' => 0, + 'total_comments' => 0, + ); + + return (object) $stats; +} + +?> diff --git a/wp-includes/images/admin-bar-sprite-rtl.png b/wp-includes/images/admin-bar-sprite-rtl.png new file mode 100644 index 0000000000000000000000000000000000000000..8f5362210f3978c73d8a435d1b80c03cdfaf5e4b GIT binary patch literal 18950 zcmbrl1yog0*EYK8Zcw@f1SF)pyQD)}>6DOeX#@%B20^-{8zdwJ1f)T_yX!8#?|<)q z#~uG2w;1P)!#;bTwf2fR=QE%A?C>|rG8m}Ds1O8U$jM5oK@bcxcy}Qqf^RfA&O*Tl zileNqGX$aGJ-uO|)O11!LRGPnka+XP(!s^S+0wz0QcgmG($UGm+{)Grg4|~_)GajB zx1R}JE}n@ig!v~ZIH(aIQ>uwa`s2saF;bym%ZE|r%n+z{;z&uslk{hY!9_>=M-iwp zqer0rMOvfEjSk8Six{|F@yfHAYPsAPeyE)m-Yh-Ms2N4-Kt@lLKexJvPYf!-!i z5x``}LX>!BVUm!g08~1v6(I%butE6d3P1Uvzpo%RIc;-UsIngF7{NxXhLF)AHr24; zmk_)cWZX|f;|_gFf$*hIv;~iu%CR>Y!Azx83pUd7Nd>$`e)R!KTbqTPVMOjZ6F#>I ztVyaUOP5Cq(dz(CtmV@o2+EEn0HZy4bie)FC)#Xu|#X3?Cn|YQ^%*dk;?hfI5V@pw)R*4 zr}$fw0d1cLi!P%M?R(p6U%|V}^X0aGv;nUT0%YK?mb-?|6u*&<#-V&No!^L&xvNKe zyd#^S{wZhLsKbcAs*3v|S~4~2^z}FDaPhd8Juk)|-~U@;zJ(KB;({uzg<-s6Uwt=t z2X~=BkFDd<@$V9XPMRG$|1coK`CA69jJiD@irmX&zkvMB<>Ed-kf9_ki^f2WNFOo; zNoEHyR*I3FbrLdnAd+?>%y**Q8FPOUr~TO_jxCP*-k;RT_+@FJI77&AB^jeJ>mN}v z&JI<}ka$N-mM+Z(Od&_C8x!QrPKK5M6nL?IWIPk9*)SN>2pyVeH#6nTrIh(&rEU{)|geCgeI<^RrIDXo)vGjI}SMf5V#J?M? zqAo=*=_RhzDurtfKYuq`YZ+_ZYaDANYg8w1Q8P?MKjdGj|6@=au$CuU z$6n`LM>I=*EvA{5sPlKUnhtypIcs9 zRcyI5;VgV5S5Eq^JwCWCvz4?3cjktU5ke5u7iPggltzd|XhGygSdpw)Kr|6a)ThD5 z!@irmnSzz#psB-H$LLKEJAnFC{wv*A5=K^yqT+($#bPwgQ_Vq*m?A@UpK@tU4)xU{ zls}0jn#CD!cGXGLEs6xbhZb9WchC^Y_tm`*N7U_B>{gM;SIw?6T5aj2M0-b63Fu4;J3`y*p>@;zB|q)>5D4xa>vJco9T+G&Eh2-0fuhpTiKWt2^Z!lYB* zh(-Lvm;YXp6mTjwvYYAK6elJn>XgWqsFuj=wsM#67iJW+N;T`=24ITy$BEXDPP%Mv~Jk>DF#vc#xdgX6T=(=uo)m(Ykp<>ce`kac_51 zcrJskgII%3i#UfqLMYh7^}#`qjJ0p>SXIKHTU6qk$ybx4l~1vaV}f~;(x0WVqO5xL zS7}%6dhL4i=n(0s=oFN8lnzqWlo|_O76>Q`DE18x4d)I|rEsMQu}{C=OxsMGPFt!s z)^pcmuKQlsU?x|+rdObQP}gD~tjDMOO-EDDphmhZqwK|0b6ImCPGO^RymCqAltp*t zaow-FqI#mr^}mR7<#l#-J|-5X&n@bE%DWHZTjPD2d_La`Az_5hV3v^Sk@-6RaKh); z;$KW1Nk0(r={ZZ>X0^~6#UB03v08^W`0}S!hI-nBjKy<{M(3c@u*h(Qz(JNIVU{$K z-i7|wHF=lw4+blqT)>$?(UaZR$U(xLCgBe zX0uf4t0JKiU{SkC!}^XjR=aGvvi_S}@ukV(bKwhNr>s@sRY9J`*NeJFLp&Yz{q=(? z-42;X!AlZ;G>`8eJs-!QLjM;q7I5JKVgYYqZ?8PPsoJAO_~*H|F)mPxM7!S`;x97t;7MpQV1J zw0kYY-^v~o7yIRdcrleye4WLP#fJIE!BVr}>W3ct$a+@tuSmaaYQB^r=)OX&6EE1( z&|{cpM5?wh(>8A(4j8Rp&BNkSb4+EeBeeiW7bY@{!Hx74?M1xs3-S}scY+Oh~EoO>oR|d0>qU|B$(CrllH-_ft^xl6oj*=d0+m|Ry zm*g1yvmc|pN>`93C_a)|kg-~er=Fxrr>UpjzBPT@q%tHtHnp?pc_%YRftAiFsOuqr zs`1Neb~|t)-r{65D&uR0k1xyhyzyLao!92OktJ&^hr=dpmz8m;$H+~A7J(7{lLnWQ z>bZ41?FQ}C3VtKI9hV)!hxiGVzUK7i)bd7so5lmr)><#~%WaZOo{@#KR*&Onk5iMG zd_oJYr=AX%J(uT8&lY^0=F1(E=ijRMUhf6-ad&lu=4&Y77cd}|q5ajs+g8YLZ==u?SZbOhO8wBkb zLlA#51U+|%GwP9sAQ5&sNihxg*}Vl1Z;kaFo}(o`i^QS#)ym?}aFMA{Zt37K3Tfl*lh9+ddLv*NC3(lMVNZHza`RRg<`-SdBqo0&nm405F=IAdYvH8XECG(M%K zrn=H8P3TZda^|jYZsrvfY>0yqe|G=%>leiKSxKCl_;|>i1Ij<0BBkbk=M?#0-@pby z5W~|w95*#JLCeTsL8M!Hd!-@wu#5vq&VOk^=jZ3p(c7t~5eT>+QEF(mlKF>%=_IZa zzTM;C%c#8bT>q-AqbA9T`vdlhFo!CRLOO~nP6>if#KXxzaks(5$zX@SFu}pG`R}du z|7Hi|K7Rg$LBgGWOmckGjdhwqdqZDt!Ux&Hzkw4)G>$q#!nwy|>v2CIubJ`Q{ZpjQ z?s4)yKR;g-f5wS6H?5Hhu`geybVFYqpQ>aYl-v>@@_9LXW2S-FVEn%BUYrtQwR-{ zwl#KzX(+F)om?0jJMLzG*GmcRnmY|6kg_sAY|G}kgW@GEG!5H;DoO=e(K`I>#u6El zd~rjM?Xb5h3_ZeR)9Rz)NnZtpJ%od;WVyzL!e=OeQx)?6z{dYC>@$R})Sx+$<_c#_ z_6^oD9>fZFwScE0xN@y;e80idZUQ{hGz-{ayXUzjC1_=3WnhPWgxoUmbp$@{89$ws zSH)+DfQ{)3T3j@Q4-z94_9o28$N;v++m6W8syaec! z)5BK#VvOeg{zZnSpA7gXjug(!%*_3gFa8nIaWLZT;No#9Hy|-^!IjvHA)u@0$o7Z$& z*TQyi{+E$)q$pxb;XvaaPbTnk$QBoOEUb_L^}nrjOhYqxdKbZ;=E>g1fbn{IdBH4G z#hV;y0sC$QHvD^H0uE|7!vJ>7tTgdPTN}w>+5u^K#SBA|)09lm9oxSC7UxrF%G11s z(G0%EBo_=jI5;qZ`-|_9>7ygbNiB(ZWaHo< zmbMIA3x)Bv4||UX1*}U-TEH%*N=RCuQ+fNq{eQGp%}=#9)0xL4Rj#{QZSj(iKe?EZ z{69n1P(Kuf^FW#F2U|cmxHL-5MSIp&up`oECvy6<2 zLQftxe=@%QjwY|WtK(@Fy&uTAgw`*KLtuO{2ys6D=$K7NOY_>#m5PL*J10$Q;-Y}{ zFSOg`2x!mPzWy&BzPovE;k&?8KG*(iOmk%tkV#WZz^1qDsznt;^!DwLv z{ClixtN+`+t1>e)QHY3${`>OtCE)okzkU1mWc`wOFn4uTRX72n*^gT39Q$IDkAhsx zF~!9q_qR)r4-+XI7S)pfZK3mHu!SXj)TVge>JboDTnClh2?aXI@dp1%A2 z>nay;+lX4v{dZ?)XXnAj78x2GlrS_T$Hm3vr32d&`MB3b1=jJEe+h&S`$hUh+IFGo zYqz<+}OjRgj?uN~(9 zZu{IQ;bA)6|BV9DK1oV~*!5rMo<5(#@?Toor`B-(bMDFS|K}XAoii@@XSd+8|8*eG zkPA4kc)Y3LhX_Mp#ITJCgEmtsk;5{Dsi&64P9<6WD$UTn%le(+Hp9=oOWTpWSUfY` zcS%wXA8rqy!(`{?ipTI6o!vQa48aV#UaiY>mUfK0w)$B_JP0i+i#Ya0FY@JW#$-Ay z{|LG#zn^U?zkDP+bftbIX;2QflK0G!#ACz3n~7Lp3ZG(_u^*Rq7kz~ zKRkp$r%D(B$%Y$!v*Vgt;9coDdL2u*fqALkps`Yu``~kUm|TWlThA(em3DA7nNBxO zMG<@YW0vypiiV-r_jSxE^vG!ne1}$AIsWw%(?((=y(8STPldHLSQeQR3+(Mt)5009 zpC=1>G~&Ok->fGvjXk%<%^VmZ-c>(y!Sv@ap^xT73bm%L(hq+S;XzTUzacMq>Bz72 zjrBzCK@$g_>R=s?dHPYrzU8=g%atS7#wvC)I^ppCa!I7rjGK!qB{;AvGsp+d|c@(gsv$OKHW&-U=Vui3AgzfGh4mLq{kZ5 z+|0~}rBtWfNZyjDe#ut=y}N_VZGOy8XkNwl1n+K*UZfKon?Sq^&q*+r>!IRzcZUR! zHjJfdl($mA`M!hSTv)9{Z*xEEB2iA~2HDisu#GZVg!@ zRj^FeEvk9a7@ikV>z+5mNBz|YO}i|mybiy}a%Ri@1rA$IFJ#MhIcLb-mjiltE82w@ zA4_0tk=W4uvs6zAkfv_VcR#x$jTWUN#?*84-uxi@y~MT`g8 z`7bzxA%gueDK-{|16ilMn`wGwiV&l_!^r4*P9<-n-KR43@D1n!#^{P35v2?TI)IMg zqB#*M7>MA3q_Y& zl$v2}8pkZw1#p%2)4X_rz&y=rSS~SH`TF(iHwFfufg?}%I%xb}UM}tG%9XB6KO%HF zKRW3cYDMaQww0<$`{QZ2z>jfx&Ym5eooRBSa4C_T30LSS1qS$VR%KG?EW}KAl9s@6 zPuWpPxGwkVnU``n9UIzwH`gmgBSUI-OnBPzyKj7SCJL0gvmWm@lOG=^vL4{Yu#X0q zD$*}toWSt1@U_mkkftV7L|*BCoH3Jb!GS@{6e4zigHiVm^G;w<76DGc{M~0%>_h1} z_j}r^8(zfbRQ^R-_@K}7Y=2?q78XAL`J+*(->%b$NuYCku}@MRv&+fyq6q0XLAg){ zzFvsQatMiiSY{ns2Rm4?zuON(9HYofH&gXdotUXQNV@sg97mV#wE;RJz-Jj^P%e&; zRd91SE+Cc&{Y1@i*G|N$!vOG1NJvOlK>=>?{A|$mJKajcOZAX+d(dB@r+z;~j(Bt* zz*Pog5`5%BoVUg#tUy>~)pN@kp{)?cW2oMe+G#9c-lD$NZD;uN<|nxhT=?`Iq5Ctt zO7*)SuIb#zy1D&(u2+e>+h2FzTpTV@xS?0OzjMHt*oWG1gz^4T=)rkE%ZzT@>4^CA zW2#j5-Or{eIxUf3#`R(7-uBDNyhqDn(~9A)Z?IfFMGWKZDu0J@3IFMEC3cq=(ZWVA z4dx}qJUct1pg_&QVNxZPx!b{o}#B2Y+7(g(yKtn`6SXv5!Fj0yHXoTk81h zMH1oh4VCFR&Cgl65U(KnmgvP57W&1ejGbMqiz0Fdm(hTUpEl)uq?ip0_MrJneU&o( z(37=z71P-tR_sRV_)?^T?njZN6iQHHD4Y$8xON*4m5a>?Q&bENwhnYO?>vf9qFuoq zvcpVX8X+>zo*F`&Cb-ygJVj*~&$tM~@keK^aQvm<-$znRBmnZ_;^I1-gle5WZ)UhH zb^>&91Om(D^tkDHM(gQ_fYBMRM=%UmARLpSpJeIA(3@5Eo>ai;A))!Z4@SEc`|=`n zzR`}uay`P-Dhb!dGil89Z0tQ<38nDecC&W=863Nx(3bL^7fG{)#2@LlH(>(jkn)zA zXIgq%FvOY>gUeg7CEEIFg_y>&xk_{RMOO{32rJYt@Y`O!IG{f+Lf3a=94^eMGpQ6M zKBxoJ@f0&kO)xHRPn0cWqFkl-HBz4y3OFvj+4W%L8Nf{EUJU*!VX*qsiL3=&Y0%A| zVSSqY?OH5BUZbzuoy+<=l;AK*t>q!E6SJigv4Ldg zT76rFs5sGy(MKrARTj%a0C?q6(41yB@m_A%O7z5;R4QIjh7hs!-KqNZK{M-#t4`sZ zUysvzlF{#NoQ75GvBy+8DgacAOkj9pdPD)(#Djvt=l_5nPq^WUdN5KpE@DQcHU+5n z2_XERaz)~lfS!sOy8l8K|C6i8WetEZ;}s<>NYnpT3nOn^k-1i#MU zIMqcFk;i#vSBr$f`tv`WKR$B$v|OgN-8(!COpNJ2M@KXvM07C#Q*@67-H+noz^pj< zX0n;UGCxBbGIxF)m6c5yHmB7olTBlDmXeg zo%}v4B$`>KhHPw@x2a3wqm~}8_Faam=g&O9p0lri`#MSg?hx-8OS6ZWDt|%#TNntV zRhV?|XvMuEN?0m1H?-}<5XvU;#}u8wKxQ-Mi0oJTVcg3gU>@ZpiVgqcnoH0xA4|Zk z{jz@QIeF9Uf>SOxe<`&Hmy(2y4b##~YE%R*F)=YeKTQ|M7X|s>%#4l}3ZJp+HQb%V zY49@R5_`6~-|Jur?_!E_K_#?Zw5Zs5%~@Rm-$)QS7yNTckiX}=A<3}pt^Qz@oRx$M zxnF-mQaLEMD)+ltR4}NeXw55T%mvItPF`LR*d+IPSWKzqyKFE1s;Q4Y9-vKHuy{0`4I@D(kScJChqLj+u_37}fxv618iyi}CEaafiRjCmUV1Y?7R#aSR%K!=~so z1(|oh8qkyBalljwP6w1cQ~cFZzu!wQ_;~rqoVea54AS$h`~eNxO6r)Of#C`fiH27} zyXNL%)2?@>ncl8J~9IY;`2r@f|{7G$$R!R8fnce;t*t(7T`Lm&%jQACLx_ z5X@r(ulnm0<`HL>+a7|N-msK#6!+B{I#SzRDLFk@+#%{cI+R~p96JlbKRgI%-@X_k zT?f14h>7^UQm8c{HZ~v~Uf_g3!-I_oQQ;T*?E+U_Y$_iDC;DAoSgj_KlfEJ|H2|xG z<;w6flP7bB?}NJtNWsn;>2gd6Witg2o}`Vhq(k5BLaN?X#A#Hj%39~~!r`$FNAwr=+y$#@`y9+X((*KfDJdlK z*U1{jmQP}_?-Zt#)RdUQzxU|k5c5&VDCqW}IVSi>%0?%SL4M@K+2Kz57elflu7;FU zpd9+SItr|YQn!JFUtqU^p$h^&ZEApufxY)8>+Kg?_LYL`9>PP#z-L8ea?~G5?YiF~ z?Tpcv9Q~5N!p{w9&)DkNDZ0KlF(X7&%nj=$$09}1DrJ!E$=YP$Z9n%rDLm&Ud*q!W zF*GuY(KhlS0GMC-@f2kKzOSE7YBeUW zt&Lno=p5hL+8WUQhVE3)SoFi9qN3sx+2e#Bu2#49_I|ec+#dZ=SIN*IPf1CE{4Xv( zrVXw$!n~nI?z#>!xw98_$IBoJ0C%_N5&R9WP>D4oDLx8sUkEG;->-`Ys5(`=wlv7D zRkl8*n~H8(C+Ru4R8M{st^THBVDa|K2s=z3ZrS(!ogjP2T-D#6@C&@LgF264g-y>% zI3Myr&LEj2jOD@U%F!{VzN%iJ6yC`&(x{%hhr`72BZuQR2ax=|x?J+}dzjQVGAP>h zypE-jfuMJN(VFI?nfG%PcgsjfumPfvXCR{>0QA|`!2wHzuW)rbU@wdSK;GAHa(S7^ zVsL}&?*VyH}L8>yp&O~Kit_NUUlJz>BzUzlWS<4ZciT{SIf#}82R__ z-#!Vqt^3&SxwO#^J6wk(WChJbH(jF?&~NNKMI@VIZp?A~K_uhIQ;})o&VNpgmFC05 zNyFxIZQj=>>-5U$vatSBQ&YXW<<0gA-+P%>wA*hqCl_12w0Sr=JJy|a_dni70A{LQ z`P5ZG*+e9=mQ=vM`e?MD^Drc>{;)TB5hYu2=Ww)ZA_30*82{2sz&~Zh4D-UU*E2>w zcdC)9h4mVdS%7uN;qlF2+g5I5y6SGA$lVH7ZB&66ELsywK7op^AO{msD)nmXjWTj> zSjlG8VLP^lma8Js*T5E9Cgrmm6RvPQ0Djdun~NJy#8Rh5)@)>pn=@bO{QEiQYd zho5w@jYUIEAUc?<((ccj!omT5Dcb0om_j@h&?G+W>jBQu0!nyLCB49`( z47d9=k9^xHoHs{&Rseczn_73uX>DDYpPo*lXbsB$_HFnpElNag$NSe*^z@s1At52N zTV|7g{;UmU2s}qXK$tUN(LRgTHmjLw16ZWS#ehd3V`R~o?JBcxwJP*B3OJrfU!mrO zwvo-#&&0X&SdjZm&$iOi(rj zUn-zC?H>VIt?L6w$+fk$_dquCD~>iL)Ziz{*)R z^t2vRRY)|^%(+1^qsDyL`X1CW^5AT^{23ufUeY$%YlXN!WmbB#FX6S;d`hZkY%>D` z1MQ*jB_(gO8RWgLTJ^D(4+zC-iACjrgHbUtG0}E&t8Z;<^DQdhZ89O815Ctnnt+?V zr-H)Z3>bz~H8Xu=F5z$2`>Dv5eku2>|F{#aLW-i32HLr&* z(gU{v(XunKE$Zs(TKVl;Pq7toy7K0DU0t0F^<%vbGrFGl0IF|SAOUZP`0;RYADv=rd&RDYV^k3D z%(J!OdLwOYanX1DM%w$R6UqH_m?t(RMRy#SJq0mc{Pt^2%~!kA6@4#~^mCyOP>vfC zx!+cqP@=jv=w162e-G`kwp~&Co~AA3zo4V*+W&~fR7!og z4Lop=W{DO;o-iBTyz8{V%wg;0VgcdlS2!IJ&$F%Nviik{539ZBFuc#oabAcV|HRt> z-1Q~X3%S&9R1WZ~93ULW^a=7F^ zXR4Dcj+NoU8ph$#Yb|Gg63%|Wb9}6K?aZ?TxE4y~;wSUGQbXuwtE{AiQ=zc-M}*78 zDfU5uHBOY=v)+?b$f8hg=5j#^GB>uhrlvFJJu+11!KKAP6t}onjPqY@75>vCm@LFk z)>N=+{FVn^Oth>7%iljeK{CuLPk8$54gPxm5*jLM^!{wEjVbRJbIJRq#l=P80#N(Q z^1XTKElhFe>lc{Ia9>*qtle4_AJoL63}S^#`lBo<#e*a!ZpEe~1JGco0 zDo{#GY`2~)P8k(>^!^pEB8-wXs*P(n>Xj$K^m;&Q?Q*Lc);|adYvY3cd?+PTfu-^N zTT|12b%2X}xCP_0QswOrR4-IsKF0dCczAU~gZw$a%_{=Lx2>1m`sOH$9aOKx@b$>jO&^dk;L?zLAH2e{gk zUc0%vl)R-?5ZF!ta8XR2pr76K8QVGUnw>wofM~)scqV={+OAzd^-NUxqgA-8a-ty8`fJ#-AlzRLgHvK;GlSa$1 zw%?r~>Lyaj;g)JKJ>9_Rt7n4)G88^8KNupWv)mRv+5y8nUkBo)ioE;)V>jFK$L3LO z{1@iN-}3X*Nr)mxRvhOMGF!;Fjmeru?>=mE`bQ5uq;c+!IhqW~c zK;7P1O7&;Mht-`z<&5HiYIIe6VCs#Wf)xwvPM_1*-yu3VISC_+XtNBSY~|yxb}Syz@06pE!}NY03fqfr&k6R>?deZo@P#0Q*+%0 z1hJ^vw#muKO(Vxn1f&9$tcQBwf~;cC0mq_M0BVA{3=JEpHclkO#CsSR7=S%4&)~2a z8H{y`R?JFE+vQ9f2EkFn)%E;SK>^*L>FIZ1qUH)!vV56)KlXaA0i0?D5GwHG-mXt) z(o$1@&&-66j3{$*ajkTrv3lFy-<)TCVll-WPD)O0n6>3z{TIoqMT*(iI+*y%#?s14 ztl<_0bbZy!({^mCsX18!+5@FwDNyU12Xk0c!@)54T1{P@YOq>Db26^qiH4Dh>C{Y@ zoM5q{qmtO%m?&;C))NFoRTpvvX|T(11IUo#ITfd z{FA^ zh$b~NfLd{)WeDn|d`bWW0r~=XsGBjdn{&LFv2c?t5COBHU(GC&*!;SMY~8+| z1k8lP!pAu~2}CV(^9{A;g=iqCumtH|1(52DDy=#-e#y#ueE;FY2fzNtg#+)j;SeGY z^EuEn!%M*EcuBcXaU9n_Zt zAH>E9=lPxf^&fA1N^}Ap!;0rX^-!UXkI+;u1N&);_R`4vaMfG=H#rCc8S(RR+u{10jTUczOOR4UGol~wP zFRz9#1q%1wTVr|8G0?O7ug+qLGfW7zr814$yc=WJz1eb%8WfaECW(t)mTPOCXOh@An4E#?N*$OgK;hEr zm+XfvZUn>xqdjjrXjqNb)6`@)8%Q9E6=SAXOs*7?ZafoVWMuS?$R#C!hXGZZyu870 zEiHGhX~T#~gPQn;mv8!XJQ`bTYMgr!p6ffZsrhma2{TJzx(mS&{l@tFs^ItV5Uep* zTIH@RQ)-i-Bfsq%S2ADSo;B59glUvH(wpmV+iw>R z={#EvukIhlXZazq90_r>EyhGb)qG#LVpNJCe?4LB{KtJ-d2*2U1$bh}>itW03#M#M*S_-p14c zaG<9-ISI5>x3OQo_=6n-*-V%FoP)}Vh?M}ffM==lX&4|9YGn#=;8*gkayKhVOV8~7 zR#M%N2wKB%GQX|c6QvEKaWOc|swgPvEic{)8hacZ9qm)%S-|ff9K5Am+y$7H4xfNv zor{}$mbx1bAxKQ}CIT{EZ1HFVc>6I0xQ}yy<*}nl`C!|Zu)wREZ2&-*0o{mPTXytr zB#3~3ptkwrzNgzzGW$Af%Mom9TH4CxEr`?UWMpKUS{fRPTAJq~NH)r#FTLibre&wb z`5**|hPt*F`T`Vq2cr{%0|WXJpNU^v|M_k%r7HhkH&H`#pLKL^Z;uSbYyIm`k-N_K z+-a;In-{P`Zn;Vp>Y`Ijeq*Gkw-d=s|MKN_ep)Az zZ_Q{rpR+!Q*TaF7%;<*+1^tg81-T#F#rN>-3`F>S{u`nCsrZ@~BOGB<-e`Uc%4UY7 z4P+rEXQR)FCVZFkN=gQaQ>3v!HJtKradByBY1xMhK6|BiW(ojz0MM@WSChK64Gk%R z1qLK6kp9d&c4Pc)aa~>7qIRaE0PaDXa9>8iR?^al)w&v#Mnt;&v zw$6TG_UJV0krZfifbc7L!Cr=q@P}NSi^TgIW40~*>i54CfLV6d0VMgS_QR?)K0ZDr zs8?&15Of-|E!lD#Ju68Ebns6}A>4-`rNUkQrsn2TSA#wf!U&Wm9IuW{*wvgV_Kr^m zS>FP}AA}&ebu}`lbfRte?tXWy2S zm1mQf`0R)w*HMziDXvxfhU3+IQf&h-(zE!#B05-FV>KkXC2k%Olth$e#)WPD) zS{nHI^d{E!#IUJ|-N-&ABe}WE$&$t|2D9`}FU8pppNr{QQ`qN*Tw5JR}$y`=Nr7s+h-Vqej4~<&fkGZoB&u(;7 z(s|j-AJe@&uCuui>*p38kFLGDcbI++45<0uty0u~CKGsu848Cba#yLQsd@hQ?)sDj zkwhr(NkLUz?KB9y_`~qFOK<4_SP%U0Tz6MpU0pkXw-!LKSUQO`y34(I-hPDulmp}A zr6c(w~Vn&8l>?m~|?RZ^yqW`27I*rH|b2?!z=laFIZyPDsG-uNHKx+)v@O zQe%a@?~eOsB{I&Iy6DQECx8C@`D8Yj*xG^2pV6TPYxn9iJ?8XBPd4u109XrL`~`n1NGlTMc1J*UdJ!%j zf5#h(fw5pC-^5$m$?_ATfsAh|T+PY&@c=!>7AAxH>I9vvxTWU;8^}U*)Y36Dkz9~v zl`D<6^9BFZxvq-7dtPde=K_UmahR|1t(;taXed(R(jTV4a(jn}_yovz6ciMERFN(N zJWe-?V%2Bh;4mQIw>_CpybKHstV*YW-fScQVEDfd^>ghc$nYmLovM0zO{^Q5xk#9M zOCbMf29O}MxY*Ltk{5`N%SDDShkXD24F9ESk*bU;mo;i}lPe&_MyNrjlzNtyX5aJ@ z83Lhhqphv&t%hbT7Pk>CCGd-mOFoxm&A<$4ruj6fTo42{MzgYt7~~TH^$C=j317cX zKACumu+L2*$U_kj#6;E9aDkv8o}%rHnxbP*l+yp;z+OW`qovtpdz`|+eIRZ+b3^)` z+uyPD+E}b&X?Im zAxeq`(nf5ZFu9l1A($Ui40qwDArJ)XzTt!un-rBY;RbS}k-UA`I^E-o6>b;LJ8YUH zNe1G9-p_#?V-fzvd9)lv@zrX_Qx?NpXTR9waNiG$evn3`vnX7N=jPeji6)iK+O1i! zSA24DTg2lXv+6FdnD2ghsbvQXgc~&+b)n%LILJ$G4Q!59&DbKLcPGC<4*a#^xZF$P zaOV!WPZfDnLXgsVU9>;2vT7?zt%8SyU$X^NX)K)|lb7X;vWHLyr%pw!E& zl@3t!i`ZEQQ&Vc@XM>l}UjnHBfZ~BEmJP0ZQokMsWWmD9ZXtm9c+_SD=!j!L4+Dgj zwpj466fhmh?C+ErX`o9=PJS6-2(Y4i4SUst_%$<8Kr~(Q&j(wlY^nrg8`QOCOJB7O zuf?>qo&oP_vAi}HsGJHQiML7|Ezkx(lai7e#Yz{Q;AGZsI0^>3T!x4^24wM_xEBhB z7I*3P7-;@Zd#g!H<$M9;M%bIm#AHoCt@^8JhIZxithsxTlV5v6?zRr6Fq&2kN*sgf z%uoHjio=d@a*;iAj%YWps!jJkk<{Sgkg9D=#X+SY8CCWQN<3VD9;|wC{1+g_w?QnX zkC9djL~$V7_TQbrG72)e3Tn$96%CX zTgHtFs`!P)#V?^m7#$x#v5h$MXP5%*Cg1}}fuP5WcHWx-=8fjtw{u1$d@fsWiuzVl zJa)_Kg=@g#`cGO*xswgxG^w=-3N8&9G=Dtjxm@&nycdV2sa@XoOGB4iO~SL=E~bFo z`uk+ZD{mY_Hrwm+@@QT%ipF(wrNxO^Kf8wH0nKQDVnZh z$d*)1NCA|~CMqSTY46GUV2m`^1hrW$yP6hAgzqlfAMa@)@GynM+JkZwg^2G*_qY3i zgO8+MaX#*O{%~s z{!ZycZSFi4fTz`|w_i|*#VY%L@aeAKw-yjRF~jB(W@g_q%O!0&x|F7Uk!-?+#`|X~G$yV2gH{_JjFiNiF*ZH{ zf_LM%D_=E-2s1*49qz~?OggAxiQ!u=bU8*+l1Yfg5&fkSDlNUA8H&t@1h3W{TfN1? ztZOvkN&_C_WCFL!dX|vW#p7t8Pll`UB4{aV2N7f&d!B2tGQB^Gg`})aHq)J>>Ez<@ znSSF-b^hy#tl$4z1-HJwu8AdlwS3mswG22sHMhBuX0Slim#0_kv@+iLIUG(*38SCChM@{cO0L~agL zm&(S`5}fs;c-;3snce zo&Z3xKLB)nm(lX>8w6$ercCKq4a&c#%u3`!_hjV#0C1}r>{U^46f~+FKTiECk zCNnP1blz$36$2})-|Lh4VVzB>E>v~Y_*w! zp_0;Y!S`9GC8_5I@8?}6l==+4_G{xP?zd#_YfHiNAGY^EAlm!@nEqiuK)*9B*k8)6 zMt@CAys2oru?8u^>Nly#XRY6@^o0EW*%g$wZt5vFV90~^U{vI%L zSg=RKAo{+uwl1~JoO^j!lDI5WqupYfjk$wEGOJP%TC@+v-_;|f ztSC^!7KhASD-ZQ6^{nmT-}yeOp22hSGSj&eAZQN}g^Cx#y@%LkCQcnD*iAt-Gjrz2m(uJ`P`Ex$8|XXiTui~eJN}1_wV0xONf)-`p; z#gnd&{w_6WQd$Uzu&?Li{jLX%s82J)IEua@@G#X5#QBe2H$U-I(y_ANFSYrA zhYrN-?aR@P-OS9^BOThV%{^!bLIeaAN?qn0^VIu+h z_17=VN*8{3?Vcwo=?Zz^ANbs=&)?ST(dsIGaV2}FZtH&X_SB>%IeQxsSCRT7#i2iK z1Tq6-`Qs^!j*{B#{iXNLe`uM(&PilPybJ)Fbg*t1zdqEYERK=$EeY^cY=c zQ*6tvVVAy=smA3nhuvSg>(C`oLI|5IzFaF`o;S986Zz;skiqWm?88uj(L))cOp|wm zxi3|s%23GLF=KW)pp)oW>^*q!ec7ICGPUJ0G_2BRBw`bc2ouQgsr&;rd{|uA^zJNG zXvME_S2%Okj+5{_;7eJp`CETeQS!Wisb|eVEumcwXjQtpIv2TT!PP3xn)dsV!E%5Uehm4_(_po!jSZMh2FtSDXGKuw7CA6hf>$murR7f#@ zXDdnE&-f$4IQ*%-#Ims^M5I*}Etv~#9k!=UR>Foy@rM`tVqBYitQtuY zDsX#qD1-j_&UiUvmBre^$1{gnWbitl`ch0%g4iTkBW+SL^H*cTvK?KJSa zTXFWgI~_4K3}jrIPb(K|L}(y^(s4uq|6Y3E*`(wAebVK zAy<2kLg>O3%Pi! zCfsR9Q&lWy2%1n1-HObxP`|6r@l}t7iE02ItM^pE+`~=mjlgkrLeQ3}@^N-9P-!_9-NTvb7hMt7m_vbgYF}a|StAUVjb$<i>A~f(wyEUWpp8t@ zWtOWnKCUePOh9+Az16BGa6FI;Z~T9oq2*bo)t)ZX2GR>Q&;q|L zvjZn&9X`_Il489^AYs8q|AVL1e|@-fZ`XnJ^>*K9A6RF9kJb03goy=clTa|H4nu=# z;+E2Dm)<|qiKsmKXJ+tm9m&~kGbK4CCg-?Kifj?F6PCynmNIw{Ub>|+ROS_5Qh2ib zzi%fE^Y2#Y?|pLn?;-ut@Xzlau2BIlV6_vEI6UuU(%GrU&f4e~dfhy=X41;yW6z#G zeP$Wl63C}5q^s7!BoJV_V~^3S>OB@0FI~G_v*cSq{#o|*n)7QvG~K)XQLuYn$G%wy zy4H2h)Y<8AR!i*iCa$F_>FXvW_=~YPUU|B}|NFNsuWjER?|pae@Q3fR?mxH#Ba@XD zUFOpPZVPS%ZVN_RO$*|}R^tK}=c28^g>ixVcwjq>fc}7O-v#aZfpVeidXd)B!nBgQ z^B2=3+)gZMGu->-k~c#Ga2$uhWBbjV@4(LJ-DC+j+iu{N$Rys=NfO7_n$3ReQB?G4 zKf{TXi@=>&s}mU`61U%eUv44O%Ga(ObXiGBsl9FxCDF8s3nGl zT{{hI#7xdHyUh=5;Pf9&N(AoYI>EE%gTJM&?pu4{HmzgXv%V=b@QLvX_L?*$O6agE zSuM^YV@I_VAL9XunmV5Ee07|xg0;E}RXCD?9k+TF&rAIbC8p-)%nWM&^LQ=;cM{i6 z0WML}pZIX1t{C^B<{!1wUEN*cn}Ln(f2sl;Yd-$|`=9Rt(5nmzzyAJ{2To+jt-sE_ z|9-!N!UbVJGvIc~R-Mzpd99Uo`~N@B+rE1lutmno&;jg7$J)(*Zx37$e`w*?_nDcQ zu{uUdzjpojTUW6+uEIv{M~xkj!LV@s&!+RwZ!E-}l~EeD)O)S{jOYI21S_5C~6MNlpg@x&sD& zJ=mB4MgQ4JIPmwxQ{Ko^*X6aRkEOdcNXE+Lr8S+hlckNdj^y|TN85y?6gMjG=OqIjy`wVrgSlDd*L=?oj~82o%)bJhE@snFms=i^&pgik4@d&_++w^3F*=1q656KwZ0R=fS1U$TaH|H2+`{~V{DYzkzii%1>ON;OZ zF^H}xH09Io(cvpyejdA778DXj5U68E0QCu83@5_VlwXafWxzli4F+kYLme*aH zxso1=aWr_^cR;cbquST?=yBdxU!@H|Z{o>jD!KwMbv!-!frDT}EzMH1v(+_VM6GxJ zT;WB;#f1aU-;%zjD_fp0g1d=s33NMGlu{;l# zssB#B?kp;@vb4A7aK`{WLiPC7mX_LDnw!()L$E>EUX}t)jg1mD{^tkbqvFSE8`!|1 z!kEQPA&~h=`^Hg`_2_5uoWS--Q-5Xl^z_Vp{ra`_9VTAEz<|;QJ}~xa3^}jtrSp12 zbeJ`mgG02su`%T#CdQK(1-^8(0d~-*IE_3*rWLguLneYB$*~o8L7_S+TAGY7*z`RR zh(3hS7i9(PW)jQi1FIP9 zI_rP|;V`ewd~em$x<8hh-t4;>H`(;`^f>Uaci`1s4B@>;!USCeJ$G06tuD{@Ep@+d zc6(Ps8YA&(C9lM9vk+wzf9}0R&Qw|&eiw3CR$FLtBC=az=^o#~|77=8ueB$pepEEd z-rj!8y-0sZiZ-kUIEieA+xk~+H@723{P1-iK)oIrD7~sRDs@@uY6!l)F$O5G2O6+v zj?EHcVkV(pPk^@`N`m^*-#^^{2oPz#2hVBh=fJdsJG=NGY)Th;K%w+|;$g+b@{&~l zVmF|xtIJ5M2%Mj;A|BS#VkCKkX?e#r<55-20m{@;TRR~jkPw(fXrNT@Fw1T4;NTzM zEhuXQqU*si#-|dNE-o(SIhqaM5c=2B?yhq3{%1e)>+1OSPx*pL* zFxnf8-AK-qX#(PcD6nzgOonPoqM4?Iv?=EBJ^T`qygOKI7$)qb^dr={5l-auR`3Y%?*j41T0Cr`6nly zDjFIkA3u^uMn=kN0J_D|qq2J9Yj3}|7<7HXo~WXvl=uC+5ntwr%3aVKEy9^~V`F0v z|I)QYa8QyzgH?e&OGO-=ECa&FF@&?W}-SlcML z8V)!4J~2_i-+=*4!bSGY&(DVevYVWc5JGha6xvWjVa7d+5f&Ckpxm#WG#F|M5G!gz z!;wg&|G{#n|7eY6?|*4;U0t2Q>w~fU=P8O_fZvn3dYt&_Es)6@_@^mf*>j z5^B!?q9cB48hm})^?sN$(S-%UnHcV>q6k<**)o+?ZIHp#{MR4jyTGYg~#f$>+>A6sll<+8t^?>_Xz(WwtE= z)^F!a1d)onXm;H3>Lnok8qdA==Qr_Borvv3sPM}xDvCxjg>>cR`;NtZC1Lh|r1b-} zn0HIBoj;)Zfk%C0o;zdo2{TT74`U91l|O)bVvg4PRR!Ff*?XmE4CB|<)=tjMRK+5o$+{1?JIgTfxw$!pis3)|AZk$|XP>EHkl5dN#Riu*xc_W%{`!GQu!Wn{#z^4&stImK%bW1XI)Bno? zykq2;Z+4+gNlSC6PNV$kjvIc<~AR>I1DqFk!fYK{`$-K^_hT1CSv8OMR()rZ;GHHWryEe$)`Dr-BcFH z^PK)!zPqI4)%%NWUT_4oNxF$<->L5D(r^tn^f)9kvhclY=GG{?Y4%;T?d9dd!RWVj7m1rDRxQusTud~D zY1ait&t1tQN$6Gf$1WV{nrTq*{TV~c{cp-k+MjnzSJ6?z0bIHa;NbDH>Hz63%(Uu9 zq`x&-i@LoZ22W?iYN!NHYX*0}Yn{iUI6Q3cJ_^B!2@(zNk?jg<4hXA}hB1N!T4cl$a-du3HSFN*GH%b3NMgr)96h?m zm3dFI&f>TLuggw2wq+Q8l5osca&Os($=4dg?QKt&WV}~`Z?B2T$Y2j0?Co_hB~b&6 zd?EDTiG=kSLS8IL1cB@7>&qG%PD~am8l9jnQYZmo0Ajp8m%2r3VuYTqGty5+WtIFU zKYsu-VPZq>L4Su!UCE$P3oAEG5CwHW+uAApg-V;mWc5~%XSQi80Q z)zClDt2YtAp zKC)AY_09l`m1}jFMN?2G^~kn^=zg=0e!t;FiMp!lF)>@C`mh+iMSE(s(6VnKCE-k5 z|MRTTUqXr2#esq=L6`g4Z5_M*rnstUl|foQr+#_Q_6fVHy1M*4uqaBptq)yVZ~QEM zwr~!rD+dgD_M{DOM;C9-(}V!IE-WwS$W4^(@|Li$LFDY4H`pfJA)FzI>L6L)inU^{ zsVJY`6(fm>qYzLLSw-!NEDsU-z`K>HX+3dWpBn1!%OTlxetF$Loi?IWyZ3mf`CMOo z427|8lDB);$;REi4#9?tzF0z?zNd6{W$d}I+#O*zzC2p=K(Mp&4G)?a6lt(dl6fm| zmz9EnAIr{_M_1ZPzSc{#IP zes8ht2_~wA=;=fDkjOv5p+}uw z4jN)3!`HE4_FO}4>#<_(x z+5hEL=ch;zvixZE5Dye`aq&Cyq3ArR#eJh0eG?N^(y06B3yE!{iA$&To0=futgM^C zU7>kULaLmRBm-yq=<*!4!&sgSwMMi1=Flo--(5Ya05fe2c}(g~C%TW}FRsl7JH;3- zC`iB6LvNZUS8jgKD)p_-G&d~I2lQayEh-O7Paf{JU76a+7+E$8^!Vi zF!V|*9>ZA5$|ZhEw>R10!qLSU`C*c<7X9(*3)iQIA8;DFKcAGJoocXK9Q%>gWMoj( zK`QTTz6sb5=mG$I<~}9J^19}`uzFFfSnPb;#QS1-qb>@OMnt2-thZFZ+|(WgSC9#oF51AV$m$hgsC=e)Ec?b>E{I zg1D2Hx#c*jG$Vt0;Z6o&)7Iv}*IpE&o;&O=y;HgT007?A`?7TyNmknIo!2v|jZx8! zxgdRfKax+h{w8)ANo+e86B83z5ul$QpbYwBF}p4g+I{(1Lw|3ySp;v*i~GvRFVT#>U3D;V+sn z{_x{A@A$sGM30TcqWdny9YE&&^ZQY!=VSn{ zo`>R}?H$0~S~>ScLbi}Y?Sp~x_o+uHcf5lX?}N1{3IFT>$1+4i(WndOW3|a*Hgp?m z2p7ptuZFvPdRX$tZ~Z!h?9i~C?vJtg<2m$uN6o|DMSw&_Ew8=aKje3@!SPVM9sF*X zscwUt-rdBp;jn#7;$)OJX$iGi5WPDxIo^=n88wq)ucJs!-Tv6@UVFfX18ZRuE%H7Z z1RC4r00Wo>o6^#)jr_0ISO3Rj6aDq;Gjo64y#xPW|4tF|*X_Um2U+|BQ{X&*Z|{Zu zyS|B9Ue`0sxkGye|* zHosGA-V@Mj5E2qvJtgmYGO(h~lEi14IOphm%ROkz&oL?Yk#Xc}%SKK~%f|B8kH6J? zZFq_*UwXEhsw5uHyN37XGdXnxq$Ve? zvfTj@GsxjEuqrb!5GFQP6ciLBA0{Lu5L-vYMh@-y%XlI}(!SW7hzp5`hzPL@etm2c zk;j&!*MptREw22=!98n?8DFg2X)sJBYTtGFCM^eW0$BD5u51+4)pJ4gvzX zM%KuY16K`lIvEvmdNbdR9IoDs3$fr)oklHYRnP|}RZelv$gyjO+_-sG)aG*uz)N|B z99+C^iZ7#vIbsjzE)06l^bvvv$)rt9O^JHS@@foVt_Sn3AdqeK_2%X#yX<2TyWEVT zjrOo^)@sO=s({@)@qcq1O&gQ7udpbqo9ZglPd}>8YuyYrsx8=m85yaCyx`8Vh#q)CL(55<9nsP4C*(MJtb%6il@CZY{rqlBic)&9?%zm17K46)grLnf8-^70<-yCAlYE%QD@)S`uhRtG_79x@9)^jqBRhkFWw?eA`U zhIt|GE%VPXlwY-&B*e>UY3v4lDvL>aXFaub642Z%lJdj5m1v-sYNCtz*md4yTuGqG zfMP}>_JYNjjZm>OBkiX26=m*k!@eddP93hEUIPbH815Lg=N8Swr3>MLb}kTciow`zL1KE zkIo*#DNXElutZ_i0v@~gxF4kzVg37XN4)~D7_Ot1eTAaV9;3{`nP!0r7HlFmBJHN` z^>Cz+eH`^-ED-1VRmqXB#2B>t5v~wJvRZNh9_?anslNc}l-oo* zdbE}K2k@Io7NoWQy5bD7IG%WA_}g#vXtw+2)a)u@_H)p+>G{%-9X)JYWPb{epdst7 z25!#=`RFQt=R3O$k(AQQZkcPmpnIg;rXSRPR}qq`4kjl`L6ef*WGiL?SPyzXwgRGHcl7>V)<^Ht}f5BHafU zVER)EEKu)p$*~5yRlI$TKkA&8QiM%vsqDnq2Pv?HRLfnwI1za^bdw}ebntszq6^wfw{2_-p-_ zn4&#*9jsdMlwt}OjEvmFwVu{xPrNKB&1;j1X#1w{PUn8|fCKV~0%C5YpnG|qXV9)n zn0m6#;bdF#PLXrxy*8BpCff1%AYt3Qzvp6xs|R|##pSz|Zd2#dc{+n@)PxSlr`CCY z{&Q~fbDiH&bNBWBC}3RAc9W8l5E`1Au#Ak1u8SY@?wCMqGp)Z^ErUNom}^rT2{la# zn%CO0k-D5q?7qD|M6zn;h-dYQ>H71%=m~u=LS_+Ad$!>_v^Ndc{euSW9UY^$)`h|u zk>S3=7twEiMb)fb=Ax6vJ16kQQ$bMd!t;8N=L$3jP{FZE{!h0~s`N2~Dh3DQ zD(z(K)xLC)w(ig%(GJ#48 zb@yoe4;`)Q?wgBj!Ay0j*2cCrQC4PVdHBs%*>vaGW;BIB`P>iJq7&q-%O;T4NaYt0 zV88z4IX(D8XSEZhUXb*X7X75O9P&7igN5FvA=gbm~R8`l-Uir?>PKs{&tNz%& z!CyewFmb`T-6@21O<=qpAt~ktM8cl|5ku=7Hu$t?WmD7HF?W?vHPQCR>P@AtSY4?` zyBYZO!l`6Q+7)8!s43&`!sXrS-wxn#r#PDtxY?UP$A>d9kJcqPlS&k&XqPKYY>Q!dr)wWah4_qxB z*3e?=<_Pe)g5|@g6R9wNt^GALZP9Rzq_~>U(P@ZHG+Nh>SI^*A&?aY8Hkh?9ZWrCp;cFeN|OI!vz%_ShaRwwo-myDwB+P|$Pn{Z*W{j7*5B)3yhs=)p#1 z^Rm6Q+eEg{GCNAk;FC7x>A{i-lqC`F%oot&7iN){oh9C(T@t(&!TKk&q_fq&`WAQR z>KI|jFxeYUFxlpX1Os`t>8+HV4d~(9Pd(0)XZrpxrQhK`sjc8BrYqx%TjOvm3`je5 zo^Mc2*J#pjO7D~AG^ohKqZBMEy)R+BWN0mA?GOBdPZ!{|(Bs#TM*Eb8!p$RIyEM!w z6lA$-V6NU~Qs2<57(42G_-geX0n4y_7|+8alP1S;MR%gj0>*`bX`cwA3e{9_e(=>g ztuik!?-#O!JgZ`A-748h<}}lZ^7a>5M+1y;MJ|giZe4L`rni~d>FINRhn?pt%ly9% z8SUkWZ|5DV(}CEKQ^Gd3`I+t?yJ~%DKT~DqU{v!Wgi1`+)O68y^m4jOFcJX|$PrRL zI|SU8^DFwo$mN8Z=-MOW!H5Tg%HSfr&FQHfR}REY#2W=G7EUKk%*GJY(37p@m+o~h zqMbEQnBDm@qZ_J<5;ge0v-MGI?)I#D>LR|7@8wdcQpU~3hrz}#%;Y~l^lYd~$0?`S zRWrsKcJhJ7FnoH;A;S7_ijn?(w6fhwxt~%)(e0?=5TohGOSmjW38l+sPcq~E2zZRj zm(SYiVg1dDmQ-_U31@AVz3(e0=PMeBOmKRugHAc^LM;N;V}z=R2?rwjSnY6fSDSB- z;)1^|cKSCvzB5hXl$V?}^I0l%={QorA*)a{wjJ{K0h12zeSU}KvYtE?c9aoL8zdar z$AEBDNp|m6>%H%D1SB-tPokfVenb$yh@|KvJ{DK8;9-64xOD7i&gu@nBOrhD%;#VcT^V?C zfBTebG);|=cYFP%`KSu?`G*NfN)qON&$Gff$qb&$GJkLV(at$4{qbC23o5CD!yPMq z^H93uBX7iN)S73iI!mr%vqivBUvT2p28SQx;U%-qYopR^v86?);TODXXV$7NBCk_5As&`j z-q<~GEg00WpsGCKuYq??F&gV^pKaI|U~5JU$w4rShwN;8Qe63MHc0gN8^=abe#J;R zb{7nUnzoa7xvh7Jze~+I#zYc}`h{uLH+P0wPp~-H=ZWy>=a`W^|Ec|r0E2C;#_B_k+-M9XQjFSC#dh)HmgglsZibo91 z%$$DJnxS;}`#G2kbx zI(Y;tA?Mujfyvt3ICjxEg=vMS{b!v#7!l~618m`DG}+4|M{L8_!gE}{uWrTu7-P`e zL$53J_=m2sAg-44-o~)6xij1`b0Ajg6%-1ccNg$Ase_tk{H*p%2&s5Wn7Tkr;t=sm zZ`dA5F=(!@deG)KAKa2GNXu?`uZT)1_U+?>5q@sYFvt?-1Nr2Q)PV@8>?>$ZTfzyC zb3CSBx+J}5Vip5mp`yD^pXXpmBAk)H#OIeVwWQA1&z{uNM;GV{eFnZ)q0EBVmdc;C zLYW9NSeUr2Y@(XV9ZWGU+3q>d$coni6=xJj=zLXeC?T<8&w9M1tk=it(;m+>G{$3Ck_7GCPGVZ1 z3$xmrDpLg@{5QxT)|^IHTikI_dzj!Y07Z-AJMv2Uh4Ip^EFX;V1fz!`vD~w^$LM+U zvd1bRTYHdfQvHV%`~wv$=5~rFjh$JnovnT!xrF$DbX2oh=5`M*Per`Y+kumgpY$D_ zb!N|eX&iu>4vH!r<;FVM%$&IFM2kIl82mDU2mQH!-y^+?(Q*(?hZuH0*;7+jzy5OR zYfttt_QrgB-zkksGmWc174eQ|!&j9Cis&Z;?Mk8^SbU$C+W}V{pOWF{R4bpQA(B)( z!uon&Z)|V3H(X)J;0zzP-LDK8^k79eZ9jBzEGcfsKRR@&^7U4vj8TnZ_VMCs)YH@D z94>Bbi-j2TS6yIIOiPR&zp1}ib;nh75OLE^CbKKLAWE5nn9Fl*rcCJ=C_Ayj*>aF6 z&XVI&%Yn&Ms55PtiQPb&lXiI=CZ8u0*iLnK5~i6?OdhePM{kU_|Hv3v$M@`+kcZ5z zsRmV}7P~gN;WTl6426Jw_Sp?rj*zicG3i$`9os;XyH60TukNV!(ywfO**c9r&r$G~ z>LeXMs&g5Ce=H~x(5cbiAF!`{k#rRFGx^Bgi*b47ib0OFHF2Z!dcym-edRhf$8WD} z4X7-U^f{Ze5xUGkIAo)NLV=OgABn+KUi-(*U0fzjR!24M!dzBI9QtD11Nz|WzWEcX zN)q7Ht3yhtqJxA9gHS&Z~lrJvjq6RQ(gJQNMfLd0&KB?;VGQHJF|yg?%?% z^49D%3hGJ;lZY-HTY}t#&@I#~o~5!>ePYw(G+|^ECwS%&$)`Bh_S_V2t(2Wv5CX+` z7b7qDlq<&%qNZnLT&;(0P~zbHtnysv)vl2>H8Z4830dSE@pN)dVr}<0JDiO{MIHyIOsn(q{)7|99yI|F){HqP@}P>w8BZ4(WF)ev1cW~tsb3iJ6ZUQB(h`| z>zh!Uy$Ic1h$H1O7f%XC=pDFu$T1+CQ4t(I`;vjcC!~gPiFd)JJ^!q*`^JE>6GgF9 zmNUimmFcE!&NG|X8!2BUj9^||D;Ud5Ji<=F6~InbK2F)~cRQiqhjIa>BAM5faf}af zw|xwhm6aRLH079HPR&Q&6CuqMbn=TTY@c(-kVUDYw4K z0!V@(J>1<S;7IJi4wl$aL(09weNlApLQ2z7j0;whzi|@xokdK>#)&A6oe3+_x=Z zOv|P`lq*Jmy3vc961TwLv|*y%%=Fb-ImFKFSs-*LbRtFu+D$_HK@J}F^>nw2zq}m} z47Q7ViLrUEZnH~n`x!+&wdcw)o-uRgB^aj~UF*wKa+QR`da4moXsY3WqgvLHFW>6H zhIdtm*E5JoDMGQ=b=sy~8K{=~JF+o}u~<-~R!$V0cwB_j%eo-?goRZk{4#7lqM!Bf z=@Wz@f?+qjm`kzT_`-1EHzZARcV(eZpDQycOYkboV^ljx)%@WTKDh%-<%ar1#&G84 zVO^9_wx&O<(`T)RtXQ3~2PM?qz*E~mYO6fMie?HkET_BKN?JYw(^$7O{BUJ@j*vLd zlC##_Js$G!8cbqs?f`-x%mLapwebPYQkpi_gTx+F&C;ppjU-K#PCb@Fp@3}>F zv{gsjP(3faWpU_+71IqEI7QH{u8DH@=J}~C18FFcc8`foE>TzqcNC;*O{Q0)X8xyzMfCo;BJcz8|Ou=UpM z4)<>xbj1_@;-e*TyWZ6d=2$J#vlJ;vxSLzLoS!sc%|HvY17D|5T`%?zn}5-%XxndE zNaiU15Or?nnRor<=?m%XfP=Yu*3QDfZLg(#1@HDoY@PjZl1Hft?rOpi7u4P>c>qET zZwS{GG5Go;#E=#BDC05G6a)g3Twbtk49Et)q53zmsWIVZTaKHeo{Dt|Ea`nTsyUYv7>8@$@OByw~{LiQl6J0`r zf*DL1Yu4RNI`OtCg$%f`%g#c)ROyE5Lk9YU1nksfoB1#@^N%q3ZU%Dhj_zbPou$Wv zgAJzY!Rnc>f31E#wZCO2ER`STv?CyLQ~uKTI7!knIVs6Qg~rV*gnJdRR(gpk? z8WX1>=R)s`vj(<%-AiF`!qm_P#WS!;=PmiPVDrSro%9@MZ2L4XkI)DM;$$6NcxR`X z3dLOi`EHe*0z+feh0WMm0gkAF10!cHxuQdc?eKV{OL>@S@y~QB)#oh`HUibS-RYPO z+Tco2qpz%`%Db7&JsEsYlQwAUf`UrlmUk^5JwZ6)dMnB)fh*P_U)HH5po*4&@E5IK zt&w#bu}@4y$eTZZW?EUIlrb2#=d6)1G1JRn|D*hys{N_VZn~M4))-C9&!u)BV$~PF zJI|LdQ`I=fv)l0Mexmna--H4HN4r1Wsh%mgiRw2+iIXxc{30Lb&@I={5oWU2Esif3 zm1-_1V9;QV_Z6rc#C>GUhUjcLdW9f>y@F5TB>peMXGqgj|vkl2?9z4PgzX~a=RfN%l&9Ej5}_YCN#6_Opg zwm$mRO2kz+Rqk#Y793UB1xaNs>&g0&64yID+AdM_d=Dr*Nwp{AGV_pa@-|nt(Gh_e zZE+`*KHT23^zXO5$-$$(@0O+|BAp^n>Fv1Eg&f9xkDoc;OsT%J@J{loT+R*=$wz7H z0>#*8ugp)FV;{mrGrgqvn4=`3%sce#O)p>4P6UZX!t?UrfNz^JSqNdjws`rH4p{{a zand9uXAwnk!i5#W64v?Rcp-b~3AZWI+;6v- z%3?f;H0)@4d5|?@Fl}t=04Hj;!kBSV!?eSfHxLC-OXmj%23-Hdei=Tkn|`#Indu{( zQj1k8kh5~1)`#oyf-_W&?)62a0D-am96>-P4lZ_)OKHf?q{#<=m;ng%xYd>W=J*Pxv*3UGb0AVOci3^PafZv&;d*C#ll_o--OjOj*$;j) z4dD#<0cF#yG=C&tTV;hpE1yr_B3K(;hS#5LyVa68gm zjXAEz4RPihSeL>o-hYRySU+Qn2rJVzv&pk!s9M8uMwDx-pqeoP=jdT7&DS*-lw3r`1)hl*}Q|w6#F5; zx{_iMmwq@N6&h^ZXs_SJ0yd7L;xWH1tfTQF>k7O^<$BnL`qSsj$fG^Fbpk_3%WLHmWNI}3ckb=V~>Q#skD3d9t02JJOr7OV7obvXkU^%p~4x2sTYr2nmTc-)tV>E~WIr z8=a6$X#001-3zYs`l8>7iYKcd-nnzkC!ZF4F)%%QG*NDK`?zh?L zQ`nv2;mEtr9&9yqBEHC<`y&w#_is!K$;tM4A)SN5s@kx+!%<;dP4o5Or_#JzR;W zc2DKgAD+xs_S+#)!QBOei2jFiWADI9hCdN`;d) zlhg<9+$g^OQWT&dpJ7L)sD;HzIiIU|(t-Z0ZsC4f+q0#&Y<{uP_uNj&PrS1W7QKx2 zRF@gP2uAb+rQ@D#;C!#8?x6F7Zb_p{n0j%f7O4hhkA92uhO66-j$YwOP@L9^FbBiD zsp)fTb^J}UUq$jpSw-bWHa_DSj5$KWY^Rzxr7pFT{b3}@Nw)bgn}@{@TFXwxLvSu~ zHGQTGgMKZJY}j)30*l>a23Jwc2!PQ@FGP2<=WJ4@hE@Ud+Z#Tor`PVLE*vh-g(Tq| ztq@z=;HwuSwG;?OQOU&g!YV|Zp6+uv>g^MG({>dHc1FQS^GWcJ((rqr)i@W5B|KOc`R5lA``NDO>gA3tlfY%8cM-R@|9R=hALO+u}uW;yl){JVKqal)qy5JhH?<<&|Zh9QkfzpniiZ`YfYB zD5s;i()PAH!081U#nK(nZSn8G8CV^s3tc~fKH zZ#LDP8k$Q|W>Waa$Sz~~Nt~|&?2g|PxXyqjjbvO1Wm{9^b{gh^BGp~WtTxG-k!NP-#x_4F%!YX@G za`Ip9fp+R@=pF%O|NpcN{JRJ6PqEv-Y5&EB1W4gMev)al4_ zC-RZI3p##x%^i8&9v5sN1Z3HJV+JTd(%2JRl9D&u`yM@zS$$aB z@c7lMSG4tz%1h`Nzutp8Ao>T2hWjgK+-Z75m@x9C@vaSzrQmOgR4MBm8$G#an~Dkw?*o{` zvPb)MbaiQ8rIXM{fk0BeOx8~~eklr7ekWV>H&o zRl@U#!G>U`XJ9e5hJgm8XJPO}|xp=6d2Y)Ci!X9&=sSXUpn yRa>z&@{zZfyLYS-Xbchj)0kEB$dbKQO*@ZEgH%pcb<}T*4%`BNe6J0dK zFh~cw0i`ieDg8CpgV1)grBK>)_I^h$%ue#0obUaf=Q;2DeecWJ&-CGcPcz{o!d}9b zwb%qL}N`Q_PZT$+iPaJ`)(=mR5nQb zw+VT{xa%!u@Rg(5@XD2s4_nV)9X1a>3}YdagRB4?$0LfYI^|7O6aZfZ+Quu9^QpoGy`NsSrGUV=cTzuuo!*W=N7Fb&f(1G~Gf+CCDRM|2^B z7PAq$GA%ek1J6txilSiOCJP+~k60?J8Vm>K(6phlfj(>1BB>@$)U3n6^b=?V9($V1 zfFi?IrBwxO^(K@H3f5O>5t&aTG?#?Hb8Jgs2GK`SB4{<~;fW;a^AYD;R&AqV)85)n>v(AcEhA z{sH^f8(SQ3+AJ6to5G$}E0~7DcbP%&ZC}XUUSIjhT2&5DIH9Kf$?I+SJ2Z!2VhPD? z4x6nT@GP5$QLn>+t*w5hA=)Ko;G?_$`TgFTnKS#EGV#S_S$7w^56InmdjCC!ga%dgEu&y19sJNEU}*vV~{KtTqkrA6qC>rrE=gOrn5OQOSW z3LO9TW`uD`bCwC!SzyETQ}=c>oc&H6rh`tK_ucju-A>2LHb(LUo`ioHonE}{3%l7c z1uuMf@%ZHj&xeWi4xya&3&B|7-0iAxbsiB7CRZVyu&5(jQTe!#dN!G!9J-eF4Np>t gwPcEr`A>iW0C727`Ns`5r2qf`07*qoM6N<$fbase_prefix); - foreach ( (array) $user as $key => $value ) { - if ( $prefix_length && substr($key, 0, $prefix_length) != $wpdb->base_prefix ) - continue; - if ( substr($key, -12, 12) != 'capabilities' ) - continue; - if ( preg_match( '/^' . $wpdb->base_prefix . '((\d+)_)?capabilities$/', $key, $match ) ) { - if ( count( $match ) > 2 ) - $blog_id = $match[ 2 ]; - else - $blog_id = 1; - $blog = get_blog_details( $blog_id ); - if ( $blog && isset( $blog->domain ) && ( $all == true || $all == false && ( $blog->archived == 0 && $blog->spam == 0 && $blog->deleted == 0 ) ) ) { - $blogs[ $blog_id ]->userblog_id = $blog_id; - $blogs[ $blog_id ]->blogname = $blog->blogname; - $blogs[ $blog_id ]->domain = $blog->domain; - $blogs[ $blog_id ]->path = $blog->path; - $blogs[ $blog_id ]->site_id = $blog->site_id; - $blogs[ $blog_id ]->siteurl = $blog->siteurl; - } - } - } - - wp_cache_add( 'blogs_of_user_' . $id . $cache_suffix, $blogs, 'users', 5 ); - return apply_filters( 'get_blogs_of_user', $blogs, $id, $all ); -} - function get_active_blog_for_user( $user_id ) { // get an active blog for user - either primary blog or from blogs list global $wpdb; $blogs = get_blogs_of_user( $user_id ); @@ -366,21 +326,6 @@ function wpmu_admin_redirect_add_updated_param( $url = '' ) { return $url; } -function is_blog_user( $blog_id = 0 ) { - global $wpdb; - - $current_user = wp_get_current_user(); - if ( !$blog_id ) - $blog_id = $wpdb->blogid; - - $cap_key = $wpdb->base_prefix . $blog_id . '_capabilities'; - - if ( is_array($current_user->$cap_key) && in_array(1, $current_user->$cap_key) ) - return true; - - return false; -} - function is_email_address_unsafe( $user_email ) { $banned_names = get_site_option( 'banned_email_domains' ); if ($banned_names && !is_array( $banned_names )) diff --git a/wp-includes/user.php b/wp-includes/user.php index 621fb978a..ef5b1e010 100644 --- a/wp-includes/user.php +++ b/wp-includes/user.php @@ -574,6 +574,167 @@ function get_users_of_blog( $id = '' ) { return get_users( array( 'blog_id' => $id ) ); } +/** + * Get the blogs a user belong to. + * + * $since 3.0.0 + * + * @param int $id User Id + * @param bool $all Whether to retrieve all blog details or an abbreviated set of details. Default is abbreviated. + * @return array A list of the user's blogs. + */ +function get_blogs_of_user( $id, $all = false ) { + global $wpdb; + + if ( !is_multisite() ) { + global $blog_id; + $blogs = array(); + $blogs[ $blog_id ]->userblog_id = $blog_id; + $blogs[ $blog_id ]->blogname = get_option('blogname'); + $blogs[ $blog_id ]->domain = ''; + $blogs[ $blog_id ]->path = ''; + $blogs[ $blog_id ]->site_id = 1; + $blogs[ $blog_id ]->siteurl = get_option('siteurl'); + return $blogs; + } + + $cache_suffix = $all ? '_all' : '_short'; + $return = wp_cache_get( 'blogs_of_user_' . $id . $cache_suffix, 'users' ); + if ( $return ) + return apply_filters( 'get_blogs_of_user', $return, $id, $all ); + + $user = get_userdata( (int) $id ); + if ( !$user ) + return false; + + $blogs = $match = array(); + $prefix_length = strlen($wpdb->base_prefix); + foreach ( (array) $user as $key => $value ) { + if ( $prefix_length && substr($key, 0, $prefix_length) != $wpdb->base_prefix ) + continue; + if ( substr($key, -12, 12) != 'capabilities' ) + continue; + if ( preg_match( '/^' . $wpdb->base_prefix . '((\d+)_)?capabilities$/', $key, $match ) ) { + if ( count( $match ) > 2 ) + $blog_id = $match[ 2 ]; + else + $blog_id = 1; + $blog = get_blog_details( $blog_id ); + if ( $blog && isset( $blog->domain ) && ( $all == true || $all == false && ( $blog->archived == 0 && $blog->spam == 0 && $blog->deleted == 0 ) ) ) { + $blogs[ $blog_id ]->userblog_id = $blog_id; + $blogs[ $blog_id ]->blogname = $blog->blogname; + $blogs[ $blog_id ]->domain = $blog->domain; + $blogs[ $blog_id ]->path = $blog->path; + $blogs[ $blog_id ]->site_id = $blog->site_id; + $blogs[ $blog_id ]->siteurl = $blog->siteurl; + } + } + } + + wp_cache_add( 'blogs_of_user_' . $id . $cache_suffix, $blogs, 'users', 5 ); + return apply_filters( 'get_blogs_of_user', $blogs, $id, $all ); +} + +function get_ordered_blogs_of_user( $user_id, $visibility = true ) { + $newblogs = $ordered = array(); + $blogs = get_blogs_of_user( $user_id ); + $order_meta = get_user_meta( $user_id, 'blog_order' ); + $visible_meta = get_user_meta( $user_id, 'blog_visibility' ); + + $order = $order_meta; + if ( !is_array( $order ) ) + $order = array(); + + $visible = $visible_meta; + if ( !is_array( $visible ) ) + $visible = array(); + + // Index the blogs by userblog_id and set the visibility flag + // Visibility is on by default, unless a linked site then off + foreach ( $blogs AS $blog ) { + $blog->visible = true; + + if ( isset( $visible[$blog->userblog_id] ) ) + $blog->visible = $visible[$blog->userblog_id]; + + $newblogs[$blog->userblog_id] = $blog; + } + + // Add the blogs to our list by order + foreach ( (array)$order AS $id ) { + // A previous change was saving the entire blog details into ordered, not just the blog ID - this detects it + if ( is_object( $id ) && isset( $id->userblog_id ) ) + $id = $id->userblog_id; + + if ( is_numeric( $id ) && isset( $newblogs[intval( $id )] ) ) { + $ordered[$id] = $newblogs[$id]; + unset( $newblogs[$id] ); + } + } + + // Add any blog not yet ordered to the end + foreach ( $newblogs AS $blog ) { + $ordered[$blog->userblog_id] = $blog; + } + + // If we're only interested in visible blogs then remove the rest + if ( $visibility ) { + foreach ( (array)$ordered AS $pos => $blog ) { + if ( $blog->visible == false ) + unset( $ordered[$pos] ); + } + } + +/* + // Set the order and visible options if the user doesn't have any, + // but rate limit it so that the global DB doesn't get hammered + if ( !is_array( $order_meta ) && ( 1 == mt_rand( 1, 10 ) ) ) + update_usermeta( $user_id, 'blog_order', array() ); + + if ( !is_array( $visible_meta ) && ( 1 == mt_rand( 1, 10 ) ) ) + update_usermeta( $user_id, 'blog_visibility', array() ); +*/ + + return apply_filters( 'ordered_blogs', $ordered ); +} + +function set_blog_visibility( $blog_id, $visible ) { + global $current_user; + + if ( is_blog_user( $blog_id ) ) { + $visibility = get_user_meta( $current_user->ID, 'blog_visibility' ); + if ( !is_array( $visibility ) ) + $visibility = array(); + + $visibility[$blog_id] = $visible; + + update_user_meta( $current_user->ID, 'blog_visibility', $visibility ); + } +} + +/** + * Checks if the current user belong to a given blog. + * + * @since 3.0.0 + * + * @param int $blog_id Blog ID + * @return bool True if the current users belong to $blog_id, false if not. + */ +function is_blog_user( $blog_id = 0 ) { + global $wpdb; + + $current_user = wp_get_current_user(); + if ( !$blog_id ) + $blog_id = $wpdb->blogid; + + $cap_key = $wpdb->base_prefix . $blog_id . '_capabilities'; + + if ( is_array($current_user->$cap_key) && in_array(1, $current_user->$cap_key) ) + return true; + + return false; +} + /** * Add meta data field to a user. * diff --git a/wp-settings.php b/wp-settings.php index bdfc58a5c..6e35a8e6e 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -133,6 +133,7 @@ require( ABSPATH . WPINC . '/class-http.php' ); require( ABSPATH . WPINC . '/widgets.php' ); require( ABSPATH . WPINC . '/nav-menu.php' ); require( ABSPATH . WPINC . '/nav-menu-template.php' ); +require( ABSPATH . WPINC . '/admin-bar.php' ); // Load multisite-specific files. if ( is_multisite() ) {