/** * Runs activation function and sets up default WP options for new blog, * a.k.a. when a registered user creates a new blog * * @param int $blog_id * @param int $user_id * * @see add_action( 'wpmu_new_blog', ... ) */ function wpmuNewBlog($blog_id, $user_id) { $this->blog_id = (int) $blog_id; $this->user_id = (int) $user_id; switch_to_blog($this->blog_id); if (!$this->isBookSetup()) { $this->wpmuActivate(); array_walk($this->opts, function ($v, $k) { if (empty($v)) { delete_option($k); } else { update_option($k, $v); } }); } // Set current metadata version to skip redundant upgrade routines update_option('pressbooks_metadata_version', \PressBooks\Metadata::$currentVersion); flush_rewrite_rules(false); do_action('pressbooks_new_blog'); restore_current_blog(); if (is_user_logged_in()) { (new \PressBooks\Catalog())->deleteCache(); \PressBooks\Redirect\location(get_admin_url($this->blog_id)); } }
/** * Force the user to edit custom-css posts in our custom editor. */ function redirect_css_editor() { $post_id = absint(@$_REQUEST['post']); if (!$post_id) { return; } // Do nothing $post = get_post($post_id); if (!$post) { return; } // Do nothing if ('custom-css' != $post->post_type) { return; } // Do nothing $redirect_url = get_bloginfo('url') . '/wp-admin/themes.php?page=pb_custom_css&slug=' . $post->post_name; \PressBooks\Redirect\location($redirect_url); }
/** * Add Book by URL */ static function formAddByUrl() { check_admin_referer('bulk-books'); // Nonce auto-generated by WP_List_Table $catalog = new static(); $user_id = $catalog->getUserId(); // Set Redirect URL if (get_current_user_id() != $user_id) { $redirect_url = get_bloginfo('url') . '/wp-admin/index.php?page=pb_catalog&user_id=' . $user_id; } else { $redirect_url = get_bloginfo('url') . '/wp-admin/index.php?page=pb_catalog'; } $url = parse_url(\PressBooks\Sanitize\canonicalize_url($_REQUEST['add_book_by_url'])); $main = parse_url(network_site_url()); if (strpos($url['host'], $main['host']) === false) { $_SESSION['pb_errors'][] = __('Invalid URL.', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } if ($url['host'] == $main['host']) { // Get slug using the path $slug = str_replace($main['path'], '', $url['path']); $slug = trim($slug, '/'); $slug = explode('/', $slug); $slug = $slug[0]; } else { // Get slug using host $slug = str_replace($main['host'], '', $url['host']); $slug = trim($slug, '.'); $slug = explode('.', $slug); $slug = $slug[0]; } $book_id = get_id_from_blogname($slug); if (!$book_id) { $_SESSION['pb_errors'][] = __('No book found.', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } // if ( ! get_blog_option( $book_id, 'blog_public' ) ) { // $_SESSION['pb_errors'][] = __( 'Book is not public', 'pressbooks' ); // \PressBooks\Redirect\location( $redirect_url ); // } $catalog->saveBook($book_id, array()); $catalog->deleteCache(); // Ok! $_SESSION['pb_notices'][] = __('Settings saved.'); // Redirect back to form \PressBooks\Redirect\location($redirect_url); }
/** * Catch form submissions * * @see pressbooks/admin/templates/import.php */ public static function formSubmit() { // -------------------------------------------------------------------------------------------------------- // Sanity check if (false == static::isFormSubmission() || false == current_user_can('edit_posts')) { // Don't do anything in this function, bail. return; } // -------------------------------------------------------------------------------------------------------- // Determine at what stage of the import we are and do something about it $redirect_url = get_bloginfo('url') . '/wp-admin/tools.php?page=pb_import'; $current_import = get_option('pressbooks_current_import'); // Revoke if (@$_GET['revoke'] && check_admin_referer('pb-revoke-import')) { self::revokeCurrentImport(); \PressBooks\Redirect\location($redirect_url); } if (@$_GET['import'] && is_array(@$_POST['chapters']) && is_array($current_import) && isset($current_import['file']) && check_admin_referer('pb-import')) { // -------------------------------------------------------------------------------------------------------- // Do Import @set_time_limit(300); $ok = false; switch ($current_import['type_of']) { case 'epub': $importer = new Epub\Epub201(); $ok = $importer->import($current_import); break; case 'wxr': $importer = new Wordpress\Wxr(); $ok = $importer->import($current_import); break; case 'odt': $importer = new Odf\Odt(); $ok = $importer->import($current_import); break; case 'docx': $importer = new Ooxml\Docx(); $ok = $importer->import($current_import); break; case 'html': $importer = new Html\Xhtml(); $ok = $importer->import($current_import); } $msg = "Tried to import a file of type {$current_import['type_of']} and "; $msg .= $ok ? 'succeeded :)' : 'failed :('; self::log($msg, $current_import); if ($ok) { // Success! Redirect to organize page $success_url = get_bloginfo('url') . '/wp-admin/admin.php?page=pressbooks'; \PressBooks\Redirect\location($success_url); } } elseif (@$_GET['import'] && !@empty($_FILES['import_file']['name']) && @$_POST['type_of'] && check_admin_referer('pb-import')) { // -------------------------------------------------------------------------------------------------------- // Set the 'pressbooks_current_import' option $allowed_file_types = array('epub' => 'application/epub+zip', 'xml' => 'application/xml', 'odt' => 'application/vnd.oasis.opendocument.text', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); $overrides = array('test_form' => false, 'mimes' => $allowed_file_types); if (!function_exists('wp_handle_upload')) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $upload = wp_handle_upload($_FILES['import_file'], $overrides); if (!empty($upload['error'])) { // Error, redirect back to form $_SESSION['pb_notices'][] = $upload['error']; \PressBooks\Redirect\location($redirect_url); } $ok = false; switch ($_POST['type_of']) { case 'wxr': $importer = new Wordpress\Wxr(); $ok = $importer->setCurrentImportOption($upload); break; case 'epub': $importer = new Epub\Epub201(); $ok = $importer->setCurrentImportOption($upload); break; case 'odt': $importer = new Odf\Odt(); $ok = $importer->setCurrentImportOption($upload); break; case 'docx': $importer = new Ooxml\Docx(); $ok = $importer->setCurrentImportOption($upload); break; } $msg = "Tried to upload a file of type {$_POST['type_of']} and "; $msg .= $ok ? 'succeeded :)' : 'failed :('; self::log($msg, $upload); if (!$ok) { // Not ok? $_SESSION['pb_errors'][] = sprintf(__('Your file does not appear to be a valid %s.', 'pressbooks'), strtoupper($_POST['type_of'])); unlink($upload['file']); } } elseif (@$_GET['import'] && @$_POST['type_of'] === 'html' && check_admin_referer('pb-import')) { // check if it's a valid url if (false == filter_var($_POST['import_html'], FILTER_VALIDATE_URL)) { $_SESSION['pb_errors'][] = __('Your URL does not appear to be valid', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } // HEAD request, check for a valid response from server $remote_head = wp_remote_head($_POST['import_html']); // Something failed if (is_wp_error($remote_head)) { error_log('\\PressBooks\\Import::formSubmit html import error, wp_remote_head()' . $remote_head->get_error_message()); $_SESSION['pb_errors'][] = $remote_head->get_error_message(); \PressBooks\Redirect\location($redirect_url); } // weebly.com (and likely some others) prevent HEAD requests, but allow GET requests if (200 !== $remote_head['response']['code'] && 405 !== $remote_head['response']['code']) { $_SESSION['pb_errors'][] = __('The website you are attempting to reach is not returning a successful response header on a HEAD request: ' . $remote_head['response']['code'], 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } // ensure the media type is HTML (not JSON, or something we can't deal with) if (false === strpos($remote_head['headers']['content-type'], 'text/html') && false === strpos($remote_head['headers']['content-type'], 'application/xhtml+xml')) { $_SESSION['pb_errors'][] = __('The website you are attempting to reach is not returning HTML content', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } // GET http request $body = wp_remote_get($_POST['import_html']); // check for wp error if (is_wp_error($body)) { $error_message = $body->get_error_message(); error_log('\\PressBooks\\Import::formSubmit error, import_html' . $error_message); $_SESSION['pb_errors'][] = $error_message; \PressBooks\Redirect\location($redirect_url); } // check for a successful response code on GET request if (200 !== $body['response']['code']) { $_SESSION['pb_errors'][] = __('The website you are attempting to reach is not returning a successful response on a GET request: ' . $body['response']['code'], 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } // add our url $body['url'] = $_POST['import_html']; $importer = new Html\Xhtml(); $ok = $importer->setCurrentImportOption($body); $msg = "Tried to upload a file of type {$_POST['type_of']} and "; $msg .= $ok ? 'succeeded :)' : 'failed :('; self::log($msg, $body['headers']); if (!$ok) { // Not ok? $_SESSION['pb_errors'][] = sprintf(__('Your file does not appear to be a valid %s.', 'pressbooks'), strtoupper($_POST['type_of'])); } } // Default, back to form \PressBooks\Redirect\location($redirect_url); }
function restrict_access() { global $wpdb; $user = wp_get_current_user(); $restricted = $wpdb->get_results('SELECT * FROM wp_sitemeta WHERE meta_key = "pressbooks_network_managers"'); if ($restricted) { $restricted = maybe_unserialize($restricted[0]->meta_value); } else { $restricted = array(); } $check_against_url = parse_url((is_ssl() ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); $redirect_url = get_site_url() . '/wp-admin/network/'; // --------------------------------------------------------------------------------------------------------------- // Don't let user go to any of these pages, under any circumstances $restricted_urls = array('themes', 'theme-(install|editor)', 'plugins', 'plugin-(install|editor)', 'settings', 'update-core'); $expr = '~/wp-admin/network/(' . implode('|', $restricted_urls) . ')\\.php$~'; if (in_array($user->ID, $restricted) && preg_match($expr, $check_against_url)) { \PressBooks\Redirect\location($redirect_url); } }
/** * Uses v1/api to get an array of public books from a PB instance * * @param string $endpoint API url * @return array of books * [2] => Array( * [title] => Brad can has book * [author] => Brad Payne * [license] => cc-by-sa * ) * [5] => Array( * [title] => Help, I'm a Book! * [author] => Frank Zappa * [license] => cc-by-nc-sa * ) */ static function getPublicBooks( $endpoint, $search = '' ) { $books = array(); $current_book = get_current_blog_id(); $domain = parse_url( $endpoint, PHP_URL_HOST ); $titles = ( ! empty( $search ) ) ? '?titles=' . $search : ''; // build the url, get list of public books $public_books = wp_remote_get( $endpoint . 'books' . '/' . $titles ); if ( is_wp_error( $public_books ) ) { error_log( '\PBT\Search\getPublicBooks error: ' . $public_books->get_error_message() ); \PressBooks\Redirect\location( get_bloginfo( 'url' ) . '/wp-admin/admin.php?page=api_search_import' ); } $public_books_array = json_decode( $public_books['body'], true ); // something goes wrong at the API level/response if ( 0 == $public_books_array['success'] ) { return; } // a valid response if ( false !== ( $public_books_array ) ) { foreach ( $public_books_array['data'] as $id => $val ) { $books[$id] = array( 'title' => $public_books_array['data'][$id]['book_meta']['pb_title'], 'author' => $public_books_array['data'][$id]['book_meta']['pb_author'], 'license' => $public_books_array['data'][$id]['book_meta']['pb_book_license'], 'domain' => $domain, ); } } // don't return results from the book where the search is happening, only if searching this instance of PB if ( isset( $books[$current_book] ) && $endpoint == network_home_url() ) { unset( $books[$current_book] ); } // cache public books for 12 hours set_transient( 'pbt-public-books-' . $domain, $books, 43200 ); return $books; }
/** * Catch form submissions * * @see pressbooks/admin/templates/export.php */ static function formSubmit() { if (false == static::isFormSubmission() || false == current_user_can('edit_posts')) { // Don't do anything in this function, bail. return; } // Set locale to UTF8 so escapeshellcmd() doesn't strip valid characters. setlocale(LC_CTYPE, 'UTF8', 'en_US.UTF-8'); putenv('LC_CTYPE=en_US.UTF-8'); // Download if (!empty($_GET['download_export_file'])) { $filename = sanitize_file_name($_GET['download_export_file']); static::downloadExportFile($filename); } // Delete if (isset($_POST['delete_export_file']) && isset($_POST['filename']) && check_admin_referer('pb-delete-export')) { $filename = sanitize_file_name($_POST['filename']); $path = static::getExportFolder(); unlink($path . $filename); delete_transient('dirsize_cache'); /** @see get_dirsize() */ \PressBooks\Redirect\location(get_bloginfo('url') . '/wp-admin/admin.php?page=pb_export'); } // Export if ('yes' == @$_GET['export'] && is_array(@$_POST['export_formats']) && check_admin_referer('pb-export')) { // -------------------------------------------------------------------------------------------------------- // Define modules $x = $_POST['export_formats']; $modules = array(); if (isset($x['pdf'])) { $modules[] = '\\PressBooks\\Export\\Prince\\Pdf'; } if (isset($x['mpdf'])) { $modules[] = '\\PressBooks\\Export\\Mpdf\\Pdf'; } if (isset($x['epub'])) { $modules[] = '\\PressBooks\\Export\\Epub\\Epub201'; // Must be set before MOBI } if (isset($x['epub3'])) { $modules[] = '\\PressBooks\\Export\\Epub3\\Epub3'; // Must be set before MOBI } if (isset($x['mobi'])) { if (!isset($x['epub'])) { // Make sure Epub source file is generated $modules[] = '\\PressBooks\\Export\\Epub\\Epub201'; // Must be set before MOBI } $modules[] = '\\PressBooks\\Export\\Mobi\\Kindlegen'; // Must be set after EPUB } if (isset($x['icml'])) { $modules[] = '\\PressBooks\\Export\\InDesign\\Icml'; } if (isset($x['xhtml'])) { $modules[] = '\\PressBooks\\Export\\Xhtml\\Xhtml11'; } if (isset($x['wxr'])) { $modules[] = '\\PressBooks\\Export\\WordPress\\Wxr'; } if (isset($x['vanillawxr'])) { $modules[] = '\\PressBooks\\Export\\WordPress\\VanillaWxr'; } if (isset($x['odt'])) { $modules[] = '\\PressBooks\\Export\\Odt\\Odt'; } // -------------------------------------------------------------------------------------------------------- // Clear cache? Range is 1 hour. $last_export = get_option('pressbooks_last_export'); $within_range = time() - $last_export; if ($within_range > 60 * 60) { \PressBooks\Book::deleteBookObjectCache(); update_option('pressbooks_last_export', time()); } // -------------------------------------------------------------------------------------------------------- // Do Export @set_time_limit(300); $redirect_url = get_bloginfo('url') . '/wp-admin/admin.php?page=pb_export'; $conversion_error = array(); $validation_warning = array(); $outputs = array(); foreach ($modules as $module) { /** @var \PressBooks\Export\Export $exporter */ $exporter = new $module(array()); if (!$exporter->convert()) { $conversion_error[$module] = $exporter->getOutputPath(); } else { if (!$exporter->validate()) { $validation_warning[$module] = $exporter->getOutputPath(); } } // Add to outputs array $outputs[$module] = $exporter->getOutputPath(); // Stats hook do_action('pressbooks_track_export', substr(strrchr($module, '\\'), 1)); } delete_transient('dirsize_cache'); /** @see get_dirsize() */ // -------------------------------------------------------------------------------------------------------- // MOBI cleanup if (isset($x['mobi']) && !isset($x['epub'])) { unlink($outputs['\\PressBooks\\Export\\Epub\\Epub201']); } // -------------------------------------------------------------------------------------------------------- // No errors? if (empty($conversion_error) && empty($validation_warning)) { // Ok! \PressBooks\Redirect\location($redirect_url); } // -------------------------------------------------------------------------------------------------------- // Error exceptions if (isset($validation_warning['\\PressBooks\\Export\\Prince\\Pdf'])) { // The PDF is garbage and we don't want the user to have it. // Delete file. Report error instead of warning. unlink($validation_warning['\\PressBooks\\Export\\Prince\\Pdf']); $conversion_error['\\PressBooks\\Export\\Prince\\Pdf'] = $validation_warning['\\PressBooks\\Export\\Prince\\Pdf']; unset($validation_warning['\\PressBooks\\Export\\Prince\\Pdf']); } // -------------------------------------------------------------------------------------------------------- // Errors :( if (count($conversion_error)) { // Conversion error \PressBooks\Redirect\location($redirect_url . '&export_error=true'); } if (count($validation_warning)) { // Validation warning \PressBooks\Redirect\location($redirect_url . '&export_warning=true'); } } }
/** * Imports user selected chapters from an instance of PB * * @param array $chapters * Array( [5] => Array( [222] => chapter ) [14] => Array( [164] => front-matter ) ) * @return type */ function import( array $chapters ) { $this->chapters = $chapters; $chapters_to_import = $this->getChapters(); libxml_use_internal_errors( true ); foreach ( $chapters_to_import as $new_post ) { // Load HTMl snippet into DOMDocument using UTF-8 hack $utf8_hack = '<?xml version="1.0" encoding="UTF-8"?>'; $doc = new \DOMDocument(); $doc->loadHTML( $utf8_hack . $new_post['post_content'] ); // Download images, change image paths $doc = $this->scrapeAndKneadImages( $doc ); $html = $doc->saveXML( $doc->documentElement ); // Remove auto-created <html> <body> and <!DOCTYPE> tags. $html = preg_replace( '/^<!DOCTYPE.+?>/', '', str_replace( array( '<html>', '</html>', '<body>', '</body>' ), array( '', '', '', '' ), $html ) ); $import_post = array( 'post_title' => $new_post['post_title'], 'post_content' => $html, 'post_type' => $new_post['post_type'], 'post_status' => $new_post['post_status'], ); // set post parent if ( 'chapter' == $new_post['post_type'] ) { $post_parent = $this->getChapterParent(); $import_post['post_parent'] = $post_parent; } // woot, woot! $pid = wp_insert_post( $import_post ); // check for errors, redirect and record if ( is_wp_error( $pid ) ) { error_log( '\PBT\Import\PBImport()->import error at `wp_insert_post()`: ' . $pid->get_error_message() ); \PBT\Search\ApiSearch::revokeCurrentImport(); \PressBooks\Redirect\location( get_bloginfo( 'url' ) . '/wp-admin/admin.php?page=api_search_import' ); } // set post metadata $this->setPostMeta( $pid, $new_post ); \PressBooks\Book::consolidatePost( $pid, get_post( $pid ) ); } return \PBT\Search\ApiSearch::revokeCurrentImport(); }
/** * Redirect away from (what we consider) bad WordPress admin pages */ function redirect_away_from_bad_urls() { if (is_super_admin()) { return; } // Do nothing $check_against_url = parse_url((is_ssl() ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); $redirect_url = get_site_url(get_current_blog_id(), '/wp-admin/'); // --------------------------------------------------------------------------------------------------------------- // If user is on post-new.php, check for valid post_type if (preg_match('~/wp-admin/post-new\\.php$~', $check_against_url)) { if (!in_array(@$_REQUEST['post_type'], \PressBooks\PostType\list_post_types())) { $_SESSION['pb_notices'][] = __('Unsupported post type.', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } } // --------------------------------------------------------------------------------------------------------------- // Don't let user go to any of these pages, under any circumstance $restricted = array('edit-tags', 'export', 'import', 'link-(manager|add)', 'nav-menus', 'options-(discussion|media|permalink|reading|writing)', 'plugin-(install|editor)', 'plugins', 'theme-editor', 'update-core', 'widgets'); // Todo: Fine grained control over: options-general.php $expr = '~/wp-admin/(' . implode('|', $restricted) . ')\\.php$~'; if (preg_match($expr, $check_against_url)) { $_SESSION['pb_notices'][] = __('You do not have sufficient permissions to access that URL.', 'pressbooks'); \PressBooks\Redirect\location($redirect_url); } }
/** * Save custom CSS to database (and filesystem) * * @see pressbooks/templates/admin/custom-css.php */ static function formSubmit() { if (false == static::isFormSubmission() || false == current_user_can('edit_theme_options')) { // Don't do anything in this function, bail. return; } // Process form if ('yes' == @$_GET['customcss'] && isset($_POST['my_custom_css']) && check_admin_referer('pb-custom-css')) { $slug = isset($_POST['slug']) ? $_POST['slug'] : 'web'; $redirect_url = get_bloginfo('url') . '/wp-admin/themes.php?page=pb_custom_css&slug=' . $slug; if (@$_POST['post_id_integrity'] != md5(NONCE_KEY . @$_POST['post_id'])) { // A hacker trying to overwrite posts?. error_log('\\PressBooks\\CustomCss::formSubmit error: unexpected value for post_id_integrity'); \PressBooks\Redirect\location($redirect_url . '&customcss_error=true'); } // Write to database $my_post = array('ID' => absint($_POST['post_id']), 'post_content' => static::cleanupCss($_POST['my_custom_css'])); $response = wp_update_post($my_post, true); if (is_wp_error($response)) { // Something went wrong? error_log('\\PressBooks\\CustomCss::formSubmit error, wp_update_post(): ' . $response->get_error_message()); \PressBooks\Redirect\location($redirect_url . '&customcss_error=true'); } // Write to file $my_post['post_content'] = stripslashes($my_post['post_content']); // We purposely send \\A0 to WordPress, but we want to send \A0 to the file system $filename = static::getCustomCssFolder() . sanitize_file_name($slug . '.css'); file_put_contents($filename, $my_post['post_content']); // Update "version" update_option('pressbooks_last_custom_css', time()); // Ok! \PressBooks\Redirect\location($redirect_url); } }