name = __( 'Robots.txt', 'all-in-one-seo-pack' ); // Human-readable name of the plugin $this->prefix = 'aiosp_robots_'; // option prefix $this->file = __FILE__; // the current file parent::__construct(); $help_text = array( 'additional' => __( 'Rule Type', 'all-in-one-seo-pack' ), 'useragent' => __( 'User Agent', 'all-in-one-seo-pack' ), 'path' => __( 'Directory Path', 'all-in-one-seo-pack' ), 'robotgen' => __( 'Robots.txt editor', 'all-in-one-seo-pack' ), ); $this->default_options = array( 'usage' => array( 'type' => 'html', 'label' => 'none', 'default' => __( 'Use the rule builder below to add rules to create a new Robots.txt file.  If you already have a Robots.txt file you should use the File Editor feature in All in One SEO Pack to edit it or you can delete your current Robots.txt file and start a new one with the rule builder below.', 'all-in-one-seo-pack' ), 'save' => false, ), 'additional' => array( 'name' => __( 'Rule Type', 'all-in-one-seo-pack' ), 'save' => false, 'type' => 'select', 'initial_options' => array( 'allow' => 'Allow', 'block' => 'Block' ), ), 'useragent' => array( 'name' => __( 'User Agent', 'all-in-one-seo-pack' ), 'save' => false, 'type' => 'text', ), 'path' => array( 'name' => __( 'Directory Path', 'all-in-one-seo-pack' ), 'save' => false, 'type' => 'text', ), 'robotgen' => array( 'name' => __( 'Generate Robots.txt', 'all-in-one-seo-pack' ), 'save' => false, 'default' => '', 'type' => 'textarea', 'cols' => 57, 'rows' => 20, 'label' => 'none', 'readonly' => 'readonly', ), 'Submit_Preview' => array( 'type' => 'submit', 'class' => 'button-primary MRL', 'name' => __( 'Add Rule', 'all-in-one-seo-pack' ) . ' »', 'nowrap' => 1, ), 'Submit_Update' => array( 'type' => 'submit', 'class' => 'button-primary', 'name' => __( 'Save Robots.txt File', 'all-in-one-seo-pack' ) . ' »', 'nowrap' => 1, ), 'Submit_Delete' => array( 'type' => 'submit', 'class' => 'button-primary', 'name' => __( 'Delete Robots.txt File', 'all-in-one-seo-pack' ) . ' »', 'nowrap' => 1, ), 'optusage' => array( 'type' => 'html', 'label' => 'none', 'default' => __( 'Click the Optimize button below and All in One SEO Pack will analyze your Robots.txt file to make sure it complies with the standards for Robots.txt files.  The results will be displayed in a table below.', 'all-in-one-seo-pack' ), 'save' => false, ), 'Submit_Opt_Update' => array( 'type' => 'submit', 'class' => 'button-primary', 'name' => __( 'Update Robots.txt File', 'all-in-one-seo-pack' ) . ' »', 'nowrap' => 1, 'style' => 'margin-left: 20px;', ), 'Submit_Opt_Preview' => array( 'type' => 'submit', 'class' => 'button-primary', 'name' => __( 'Disregard Changes', 'all-in-one-seo-pack' ) . ' »', 'nowrap' => 1, ), 'Submit_Optimize' => array( 'type' => 'submit', 'class' => 'button-primary', 'name' => __( 'Optimize', 'all-in-one-seo-pack' ) . ' »', ), ); if ( ! empty( $help_text ) ) { foreach ( $help_text as $k => $v ) { $this->default_options[ $k ]['help_text'] = $v; } } $this->locations = array( 'generator' => array( 'name' => 'Robots.txt', 'type' => 'settings', 'options' => array( 'usage', 'additional', 'useragent', 'path', 'Submit_Preview', 'Submit_Update', 'Submit_Delete', 'robotgen', 'optusage', 'Submit_Opt_Update', 'Submit_Opt_Preview', 'Submit_Optimize', ), ), ); $this->layout = array( 'default' => array( 'name' => __( 'Create a Robots.txt File', 'all-in-one-seo-pack' ), 'options' => array( 'usage', 'additional', 'useragent', 'path', 'Submit_Preview', 'Submit_Update', 'Submit_Delete', 'robotgen', ), // this is set below, to the remaining options -- pdb ), ); $this->layout['optimize'] = array( 'name' => __( 'Optimize your Robots.txt File', 'all-in-one-seo-pack' ), 'options' => array( 'optusage', 'Submit_Optimize' ), ); if ( isset( $_POST['Submit_Optimize'] ) ) { $this->layout['optimize']['options'] = array( 'optusage', 'Submit_Opt_Update', 'Submit_Opt_Preview', 'robothtml', ); $this->default_options['optusage']['default'] = __( 'Your Robots.txt file has been optimized.  Here are the results and recommendations.  Click the Update Robots.txt File button below to write these changes to your Robots.txt file.  Click the Disregard Changes button to ignore these recommendations and keep your current Robots.txt file.', 'all-in-one-seo-pack' ); } // load initial options / set defaults $this->update_options(); add_action( $this->prefix . 'settings_update', array( $this, 'do_robots' ), 10, 2 ); add_filter( $this->prefix . 'display_options', array( $this, 'filter_options' ), 10, 2 ); add_filter( $this->prefix . 'submit_options', array( $this, 'filter_submit' ), 10, 2 ); add_filter( $this->prefix . 'display_settings', array( $this, 'filter_settings' ), 10, 2 ); } function filter_settings( $settings, $location ) { if ( $location == 'generator' ) { $prefix = $this->get_prefix( $location ) . $location . '_'; if ( isset( $_POST['Submit_Optimize'] ) ) { if ( isset( $settings[ $prefix . 'robotgen' ] ) ) { $settings[ $prefix . 'robotgen' ]['type'] = 'hidden'; $settings[ $prefix . 'robotgen' ]['label'] = 'none'; $settings[ $prefix . 'robotgen' ]['help_text'] = ''; $settings[ $prefix . 'robothtml' ] = array( 'name' => __( 'Robots.txt', 'all-in-one-seo-pack' ), 'save' => false, 'default' => '', 'type' => 'html', 'label' => 'none', 'style' => 'margin-top:10px;', ); } } } return $settings; } function filter_submit( $submit, $location ) { if ( $location == 'generator' ) { unset( $submit['Submit_Default'] ); $submit['Submit']['type'] = 'hidden'; } return $submit; } /** * Returns the sitemap filename; * * @return bool */ function get_sitemap_filename() { global $aioseop_options; if ( isset( $aioseop_options['modules']['aiosp_sitemap_options']['aiosp_sitemap_filename'] ) ) { return $aioseop_options['modules']['aiosp_sitemap_options']['aiosp_sitemap_filename']; } return false; } /** * Filters the options. * * @todo Much of this couldn't be considered filtering options, and should be extracted to other functions. * @since ?? * @since 2.3.6 */ function filter_options( $options, $location ) { if ( $location ) { $prefix = $this->get_prefix( $location ) . $location . '_'; } if ( $location === 'generator' ) { $optimize = false; $robotgen = ''; if ( ! empty( $_POST[ $prefix . 'robotgen' ] ) ) { $robotgen = str_replace( "\r\n", "\n", $_POST[ $prefix . 'robotgen' ] ); } if ( isset( $_POST['Submit_Preview'] ) ) { $options[ $prefix . 'robotgen' ] = $robotgen; } if ( ! isset( $_POST['Submit_Preview'] ) ) { if ( isset( $_POST['Submit_Optimize'] ) && ! isset( $_POST['Submit_Delete'] ) && ! isset( $_POST['Submit_Update'] ) && ! isset( $_POST['Submit_Opt_Update'] ) ) { $optimize = true; } if ( ! isset( $options[ $prefix . 'robotgen' ] ) || empty( $options[ $prefix . 'robotgen' ] ) ) { if ( $optimize ) { $options[ $prefix . 'robotgen' ] = $robotgen; } if ( empty( $options[ $prefix . 'robotgen' ] ) ) { $options = $this->load_files( $options, array( 'robotgen' => 'robots.txt' ), $prefix ); } } } $access = ( get_option( 'blog_public' ) ) ? 'allow' : 'block'; if ( $access ) { global $aioseop_options; $sitemapurl = ''; $sitemap_filename = $this->get_sitemap_filename(); if ( $sitemap_filename ) { $sitemapurl = trailingslashit( get_home_url() ) . $sitemap_filename . '.xml'; } $allow_rule = "Sitemap: $sitemapurl \n\n# global\nUser-agent: *\nDisallow: /xmlrpc.php\n\n"; $block_rule = "# global\nUser-agent: *\nDisallow: /\n\n"; if ( empty( $options[ $prefix . 'robotgen' ] ) ) { $options[ $prefix . 'robotgen' ] = ''; } if ( isset( $_POST['Submit_Preview'] ) && ( ( $options[ $prefix . 'robotgen' ] == $allow_rule ) || ( $options[ $prefix . 'robotgen' ] == $block_rule ) ) ) { $options[ $prefix . 'robotgen' ] = ''; } if ( $access === 'block' && empty( $options[ $prefix . 'robotgen' ] ) ) { $options[ $prefix . 'robotgen' ] .= $block_rule; } elseif ( $access === 'allow' && empty( $options[ $prefix . 'robotgen' ] ) ) { $options[ $prefix . 'robotgen' ] .= $allow_rule; } } foreach ( array( 'ad' => 'additional', 'ua' => 'useragent', 'dp' => 'path' ) as $k => $v ) { if ( isset( $_POST[ $prefix . $v ] ) ) { $$k = $_POST[ $prefix . $v ]; } } if ( ! empty( $ad ) && ! empty( $ua ) && ! empty( $dp ) ) { if ( $ad === 'allow' ) { $ad = 'Allow: '; } else { $ad = 'Disallow: '; } $options[ $prefix . 'robotgen' ] .= "User-agent: $ua\n$ad $dp\n\n"; } $file = explode( "\n", $options[ $prefix . 'robotgen' ] ); if ( $optimize ) { $rules = $this->parse_robots( $file ); $user_agents = $this->get_robot_user_agents( $rules ); foreach ( $user_agents as $ua => $rules ) { $user_agents[ $ua ]['disallow'] = $this->opt_robot_rule( $rules['disallow'] ); $user_agents[ $ua ]['allow'] = $this->opt_robot_rule( $rules['allow'] ); } $rules = $this->flatten_user_agents( $user_agents ); unset( $user_agents ); foreach ( $rules as $r ) { $r['disallow'] = $this->opt_robot_rule( $r['disallow'] ); $r['allow'] = $this->opt_robot_rule( $r['allow'] ); } $options[ $prefix . 'robotgen' ] = $this->output_robots( $rules ); $file2 = explode( "\n", $options[ $prefix . 'robotgen' ] ); $options[ $prefix . 'robothtml' ] = '
' . $this->annotate_robots_html( $file, true, __( 'Current File', 'all-in-one-seo-pack' ) ) . '' . $this->annotate_robots_html( $file2, true, __( 'Proposed Changes', 'all-in-one-seo-pack' ) ) . '
'; } else { $options[ $prefix . 'robothtml' ] = $this->annotate_robots_html( $file, true, __( 'Current File', 'all-in-one-seo-pack' ) ); } } return $options; } function do_robots( $options, $location ) { if ( $location ) { $prefix = $this->get_prefix( $location ) . $location . '_'; } if ( $location === 'generator' ) { if ( isset( $_POST['Submit_Update'] ) || isset( $_POST['Submit_Opt_Update'] ) ) { $this->save_files( array( 'robotgen' => 'robots.txt' ), $prefix ); } elseif ( isset( $_POST['Submit_Delete'] ) ) { $this->delete_files( array( 'robotgen' => 'robots.txt' ) ); } } } function annotate_robots_html( $file, $show_help = false, $title = '' ) { $robots = $this->annotate_robots( $file ); if ( ! empty( $robots ) ) { $buf = ''; if ( ! empty( $title ) ) { $buf .= ''; } $buf .= ''; $buf .= ''; $buf .= ''; $buf .= ''; foreach ( $robots as $r ) { $class = 'robots'; $status = '#9cf975'; $help = ''; if ( ! $r['valid'] || ! $r['strict'] ) { if ( ! $r['strict'] ) { $class .= ' quirks'; $status = 'yellow'; } if ( ! $r['valid'] ) { $class .= ' invalid'; $status = '#f9534a'; } if ( $show_help ) { $help = '' . '
'; } } $buf .= ""; } $buf .= ''; $buf .= '
' . $title . '
ParameterStatus
{$help}
'; if ( $show_help ) { $buf .= '

' . __( 'Legend', 'all-in-one-seo-pack' ) . '

' . __( 'More Information', 'all-in-one-seo-pack' ) . '
'; } } else { $buf = '

Your Robots.txt file is either empty, cannot be found, or has invalid data.

'; } return $buf; } function annotate_robots( $robots ) { $state = 0; $rules = array(); foreach ( $robots as $l ) { $l = trim( $l ); if ( empty( $l[0] ) ) { if ( $state > 1 ) { $rules[] = array( 'state' => 0, 'type' => 'blank', 'content' => $l, 'valid' => true, 'strict' => true, ); $state = 0; } } elseif ( $l[0] === '#' ) { if ( $state < 1 ) { $state = 1; } $rules[] = array( 'state' => $state, 'type' => 'comment', 'content' => $l, 'valid' => true, 'strict' => true, ); } elseif ( stripos( $l, 'sitemap' ) === 0 ) { $state = 2; $rules[] = array( 'state' => $state, 'type' => 'sitemap', 'content' => $l, 'valid' => true, 'strict' => false, ); } elseif ( stripos( $l, 'crawl-delay' ) === 0 ) { $state = 3; $rules[] = array( 'state' => $state, 'type' => 'crawl-delay', 'content' => $l, 'valid' => true, 'strict' => false, ); } elseif ( stripos( $l, 'user-agent' ) === 0 ) { $state = 3; $rules[] = array( 'state' => $state, 'type' => 'user-agent', 'content' => $l, 'valid' => true, 'strict' => true, ); } elseif ( stripos( $l, 'useragent' ) === 0 ) { $state = 3; $rules[] = array( 'state' => $state, 'type' => 'user-agent', 'content' => $l, 'valid' => true, 'strict' => false, ); } elseif ( stripos( $l, 'disallow' ) === 0 ) { if ( $state < 3 ) { $rules[] = array( 'state' => $state, 'type' => 'disallow', 'content' => $l, 'valid' => false, 'strict' => false, ); continue; } $state = 3; $rules[] = array( 'state' => $state, 'type' => 'disallow', 'content' => $l, 'valid' => true, 'strict' => true, ); } elseif ( stripos( $l, 'allow' ) === 0 ) { if ( $state < 3 ) { $rules[] = array( 'state' => $state, 'type' => 'allow', 'content' => $l, 'valid' => false, 'strict' => false, ); continue; } $state = 3; $rules[] = array( 'state' => $state, 'type' => 'allow', 'content' => $l, 'valid' => true, 'strict' => false, ); } else { $rules[] = array( 'state' => $state, 'type' => 'unknown', 'content' => $l, 'valid' => false, 'strict' => false, ); } } return $rules; } function parse_annotated_robots( $robots ) { $state = 0; $rules = array(); $opts = array( 'sitemap', 'crawl-delay', 'user-agent', 'allow', 'disallow', 'comment' ); $rule = array(); foreach ( $opts as $o ) { $rule[ $o ] = array(); } $blank_rule = $rule; foreach ( $robots as $l ) { switch ( $l['type'] ) { case 'blank': if ( $state >= 1 ) { if ( ( $state === 1 ) && ( empty( $rule['user-agent'] ) ) ) { $rule['user-agent'] = array( null ); } $rules[] = $rule; $rule = $blank_rule; } continue; case 'comment': $rule['comment'][] = $l['content']; continue; case 'sitemap': $rule['sitemap'][] = trim( substr( $l['content'], 8 ) ); break; case 'crawl-delay': $rule['crawl-delay'][] = trim( substr( $l['content'], 12 ) ); break; case 'user-agent': if ( $l['strict'] ) { $ua = trim( substr( $l['content'], 11 ) ); } else { $ua = trim( substr( $l['content'], 10 ) ); } $rule['user-agent'][] = $ua; break; case 'disallow': if ( $l['valid'] ) { $rule['disallow'][] = trim( substr( $l['content'], 9 ) ); break; } continue; case 'allow': if ( $l['valid'] ) { $rule['allow'][] = trim( substr( $l['content'], 6 ) ); break; } continue; case 'unknown': default: } $state = $l['state']; } if ( ( $state === 1 ) && ( empty( $rule['user-agent'] ) ) ) { $rule['user-agent'] = array( null ); } if ( $state >= 1 ) { $rules[] = $rule; } return $rules; } function parse_robots( $robots ) { return $this->parse_annotated_robots( $this->annotate_robots( $robots ) ); } function get_robot_user_agents( $rules ) { $opts = array( 'sitemap', 'crawl-delay', 'user-agent', 'allow', 'disallow', 'comment' ); $user_agents = array(); foreach ( $rules as $r ) { if ( ! empty( $r['sitemap'] ) && empty( $r['user-agent'] ) ) { $r['user-agent'] = array( null ); } foreach ( $r['user-agent'] as $ua ) { if ( ! isset( $user_agents[ $ua ] ) ) { $user_agents[ $ua ] = array(); } foreach ( $opts as $o ) { if ( ! isset( $user_agents[ $ua ][ $o ] ) ) { $user_agents[ $ua ][ $o ] = $r[ $o ]; } else { $user_agents[ $ua ][ $o ] = array_merge( $user_agents[ $ua ][ $o ], $r[ $o ] ); } } } } return $user_agents; } function flatten_user_agents( $user_agents ) { $rules = array(); foreach ( $user_agents as $ua => $r ) { $r['user-agent'] = array( $ua ); $rules[] = $r; } return $rules; } function opt_robot_rule( $dis ) { if ( is_array( $dis ) ) { // unique rules only $dis = array_unique( $dis, SORT_STRING ); $pd = null; foreach ( $dis as $k => $d ) { $d = trim( $d ); if ( ! empty( $pd ) && ! empty( $d ) ) { if ( strpos( $d, $pd ) === 0 ) { unset( $dis[ $k ] ); continue; // get rid of subpaths of $pd } } $l = strlen( $d ); if ( ( $l > 0 ) && ( $d[ $l - 1 ] !== '/' ) ) { continue; } $pd = $d; // only allow directory paths for $pd } } return $dis; } function output_robots( $rules ) { $robots = ''; foreach ( $rules as $r ) { foreach ( $r['comment'] as $c ) { $robots .= "$c\n"; } foreach ( $r['user-agent'] as $u ) { if ( $u != '' ) { $robots .= "User-agent: $u\n"; } } foreach ( $r['crawl-delay'] as $c ) { $robots .= "Crawl-Delay: $c\n"; } foreach ( $r['allow'] as $a ) { $robots .= "Allow: $a\n"; } foreach ( $r['disallow'] as $d ) { $robots .= "Disallow: $d\n"; } foreach ( $r['sitemap'] as $s ) { $robots .= "Sitemap: $s\n"; } $robots .= "\n"; } return $robots; } } }