Make WordPress Core

Changeset 11350


Ignore:
Timestamp:
05/16/2009 02:04:36 AM (15 years ago)
Author:
ryan
Message:

Support IIS 7.0 URL Rewrite Module. Props ruslany. Hat tips to peaceablewhale, hakre, Denis-de-Bernardy, sivel. fixes #8974

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/includes/misc.php

    r11170 r11350  
    137137
    138138/**
     139 * Updates the IIS web.config file with the current rules if it is writable.
     140 * If the permalinks do not require rewrite rules then the rules are deleted from the web.config file.
     141 *
     142 * @since 2.8.0
     143 *
     144 * @return bool True if web.config was updated successfully
     145 */
     146function iis7_save_url_rewrite_rules(){
     147    global $wp_rewrite;
     148   
     149    $home_path = get_home_path();
     150    $web_config_file = $home_path . 'web.config';
     151
     152    // Using win_is_writable() instead of is_writable() because of a bug in Windows PHP
     153    if ( ( ! file_exists($web_config_file) && win_is_writable($home_path) && $wp_rewrite->using_mod_rewrite_permalinks() ) || win_is_writable($web_config_file) ) {
     154        if ( iis7_supports_permalinks() ) {
     155            $rule = $wp_rewrite->iis7_url_rewrite_rules();
     156            if ( ! empty($rule) ) {
     157                return iis7_add_rewrite_rule($web_config_file, $rule);
     158            } else {
     159                return iis7_delete_rewrite_rule($web_config_file);
     160            }
     161        }
     162    }
     163    return false;
     164}
     165
     166/**
    139167 * {@internal Missing Short Description}}
    140168 *
     
    371399    }
    372400}
     401
     402/**
     403 * Check if IIS 7 supports pretty permalinks
     404 *
     405 * @since 2.8.0
     406 *
     407 * @return bool
     408 */
     409function iis7_supports_permalinks() {
     410    global $is_iis7;
     411
     412    $supports_permalinks = false;   
     413    if ( $is_iis7 ) {
     414        /* First we check if the DOMDocument class exists. If it does not exist,
     415         * which is the case for PHP 4.X, then we cannot easily update the xml configuration file,
     416         * hence we just bail out and tell user that pretty permalinks cannot be used.
     417         * This is not a big issue because PHP 4.X is going to be depricated and for IIS it
     418         * is recommended to use PHP 5.X NTS.
     419         * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When
     420         * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'.
     421         * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs
     422         * via ISAPI then pretty permalinks will not work.
     423         */
     424        $supports_permalinks = class_exists('DOMDocument') && isset($_SERVER['IIS_UrlRewriteModule']) && ( php_sapi_name() == 'cgi-fcgi' );
     425    }
     426       
     427    return apply_filters('iis7_supports_permalinks', $supports_permalinks);
     428}
     429
     430/**
     431 * Check if rewrite rule for WordPress already exists in the IIS 7 configuration file
     432 *
     433 * @since 2.8.0
     434 *
     435 * @return bool
     436 * @param string $filename The file path to the configuration file
     437 */
     438function iis7_rewrite_rule_exists($filename) { 
     439    if ( ! file_exists($filename) )
     440        return false;   
     441    if ( ! class_exists('DOMDocument') )
     442        return false;
     443   
     444    $doc = new DOMDocument();
     445    if ( $doc->load($filename) === false )
     446        return false;
     447    $xpath = new DOMXPath($doc);
     448    $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[@name=\'wordpress\']');
     449    if ( $rules->length == 0 )
     450        return false;
     451    else
     452        return true;   
     453}
     454
     455/**
     456 * Delete WordPress rewrite rule from web.config file if it exists there
     457 *
     458 * @since 2.8.0
     459 *
     460 * @param string $filename Name of the configuration file
     461 * @return bool
     462 */
     463function iis7_delete_rewrite_rule($filename) { 
     464    // If configuration file does not exist then rules also do not exist so there is nothing to delete
     465    if ( ! file_exists($filename) )
     466        return true;
     467   
     468    if ( ! class_exists('DOMDocument') )
     469        return false;
     470   
     471    $doc = new DOMDocument();
     472    $doc->preserveWhiteSpace = false;
     473
     474    if ( $doc -> load($filename) === false )
     475        return false;
     476    $xpath = new DOMXPath($doc);
     477    $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[@name=\'wordpress\']');
     478    if ( $rules->length > 0 ) {
     479        $child = $rules->item(0);
     480        $parent = $child->parentNode;
     481        $parent->removeChild($child);
     482        $doc->formatOutput = true;
     483        saveDomDocument($doc, $filename);
     484    }
     485    return true;
     486}
     487
     488/**
     489 * Add WordPress rewrite rule to the IIS 7 configuration file.
     490 *
     491 * @since 2.8.0
     492 *
     493 * @param string $filename The file path to the configuration file
     494 * @param string $rewrite_rule The XML fragment with URL Rewrite rule
     495 * @return bool
     496 */
     497function iis7_add_rewrite_rule($filename, $rewrite_rule) { 
     498    if ( ! class_exists('DOMDocument') )
     499        return false;
     500   
     501    // If configuration file does not exist then we create one.
     502    if ( ! file_exists($filename) ) {
     503        $fp = fopen( $filename, 'w');
     504        fwrite($fp, '<configuration/>');
     505        fclose($fp);
     506    }
     507   
     508    $doc = new DOMDocument();
     509    $doc->preserveWhiteSpace = false;
     510
     511    if ( $doc->load($filename) === false )
     512        return false;
     513   
     514    $xpath = new DOMXPath($doc);
     515
     516    // First check if the rule already exists as in that case there is no need to re-add it
     517    $wordpress_rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[@name=\'wordpress\']');
     518    if ( $wordpress_rules->length > 0 )
     519        return true;
     520
     521    // Check the XPath to the rewrite rule and create XML nodes if they do not exist
     522    $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite/rules');
     523    if ( $xmlnodes->length > 0 ) {
     524        $rules_node = $xmlnodes->item(0);
     525    } else {
     526        $rules_node = $doc->createElement('rules');
     527       
     528        $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite');
     529        if ( $xmlnodes->length > 0 ) {
     530            $rewrite_node = $xmlnodes->item(0);
     531            $rewrite_node->appendChild($rules_node);
     532        } else {
     533            $rewrite_node = $doc->createElement('rewrite');
     534            $rewrite_node->appendChild($rules_node);
     535
     536            $xmlnodes = $xpath->query('/configuration/system.webServer');
     537            if ( $xmlnodes->length > 0 ) {
     538                $system_webServer_node = $xmlnodes->item(0);
     539                $system_webServer_node->appendChild($rewrite_node);
     540            } else {
     541                $system_webServer_node = $doc->createElement('system.webServer');
     542                $system_webServer_node->appendChild($rewrite_node);
     543       
     544                $xmlnodes = $xpath->query('/configuration');
     545                if ( $xmlnodes->length > 0 ) {
     546                    $config_node = $xmlnodes->item(0);
     547                    $config_node->appendChild($system_webServer_node);
     548                } else {
     549                    $config_node = $doc->createElement('configuration');
     550                    $doc->appendChild($config_node);
     551                    $config_node->appendChild($system_webServer_node);
     552                }
     553            }
     554        }
     555    }
     556   
     557    $rule_fragment = $doc->createDocumentFragment();
     558    $rule_fragment->appendXML($rewrite_rule);
     559    $rules_node->appendChild($rule_fragment);
     560
     561    $doc->formatOutput = true; 
     562    saveDomDocument($doc, $filename);
     563
     564    return true;   
     565}
     566
     567/**
     568 * Saves the XML document into a file
     569 *
     570 * @since 2.8.0
     571 *
     572 * @param DOMDocument $doc
     573 * @param string $filename
     574 */
     575function saveDomDocument($doc, $filename) {
     576    $config = $doc->saveXML();
     577    $config = preg_replace("/([^\r])\n/", "$1\r\n", $config);
     578    $fp = fopen($filename, 'w');
     579    fwrite($fp, $config);
     580    fclose($fp);
     581}
     582
     583/**
     584 * Workaround for Windows bug in is_writable() function
     585 *
     586 * @since 2.8.0
     587 *
     588 * @param object $path
     589 * @return bool
     590 */
     591function win_is_writable($path) {
     592    /* will work in despite of Windows ACLs bug
     593     * NOTE: use a trailing slash for folders!!!
     594     * see http://bugs.php.net/bug.php?id=27609
     595     * see http://bugs.php.net/bug.php?id=30931
     596     */
     597
     598    if ( $path{strlen($path)-1} == '/' ) // recursively return a temporary file path
     599        return win_is_writable($path . uniqid(mt_rand()) . '.tmp');
     600    else if ( is_dir($path) )
     601        return win_is_writable($path . '/' . uniqid(mt_rand()) . '.tmp');
     602    // check tmp file for read/write capabilities
     603    $rm = file_exists($path);
     604    $f = @fopen($path, 'a');
     605    if ($f===false)
     606        return false;
     607    fclose($f);
     608    if ( ! $rm )
     609        unlink($path);
     610    return true;
     611}
    373612?>
  • trunk/wp-admin/options-permalink.php

    r11204 r11350  
    7171
    7272$home_path = get_home_path();
     73$iis7_permalinks = iis7_supports_permalinks();
    7374
    7475if ( isset($_POST['permalink_structure']) || isset($_POST['category_base']) ) {
     
    101102$tag_base = get_option( 'tag_base' );
    102103
    103 if ( (!file_exists($home_path.'.htaccess') && is_writable($home_path)) || is_writable($home_path.'.htaccess') )
    104     $writable = true;
    105 else
    106     $writable = false;
    107 
    108 if ($wp_rewrite->using_index_permalinks())
     104if ( $iis7_permalinks ) {
     105    if ( ( ! file_exists($home_path . 'web.config') && win_is_writable($home_path) ) || win_is_writable($home_path . 'web.config') )
     106        $writable = true;
     107    else
     108        $writable = false;
     109} else {
     110    if ( ( ! file_exists($home_path . '.htaccess') && is_writable($home_path) ) || is_writable($home_path . '.htaccess') )
     111        $writable = true;
     112    else
     113        $writable = false;
     114}
     115
     116if ( $wp_rewrite->using_index_permalinks() )
    109117    $usingpi = true;
    110118else
     
    116124<?php if (isset($_POST['submit'])) : ?>
    117125<div id="message" class="updated fade"><p><?php
    118 if ( $permalink_structure && !$usingpi && !$writable )
    119     _e('You should update your .htaccess now.');
    120 else
    121     _e('Permalink structure updated.');
    122 ?></p></div>
     126if ( $iis7_permalinks ) {
     127    if ( $permalink_structure && ! $usingpi && ! $writable )
     128        _e('You should update your web.config now');
     129    else if ( $permalink_structure && ! $usingpi && $writable)
     130        _e('Permalink structure updated. Remove write access on web.config file now!');
     131    else
     132        _e('Permalink structure updated');
     133} else {
     134    if ( $permalink_structure && ! $usingpi && ! $writable )
     135        _e('You should update your .htaccess now.');
     136    else
     137        _e('Permalink structure updated.');
     138}
     139?>
     140</p></div>
    123141<?php endif; ?>
    124142
     
    135153$prefix = '';
    136154
    137 if ( ! got_mod_rewrite() )
     155if ( ! got_mod_rewrite() && ! $iis7_permalinks )
    138156    $prefix = '/index.php';
    139157
     
    180198
    181199<h3><?php _e('Optional'); ?></h3>
    182 <?php if ($is_apache) : ?>
     200<?php if ( $is_apache || $iis7_permalinks ) : ?>
    183201    <p><?php _e('If you like, you may enter custom structures for your category and tag <abbr title="Universal Resource Locator">URL</abbr>s here. For example, using <kbd>topics</kbd> as your category base would make your category links like <code>http://example.org/topics/uncategorized/</code>. If you leave these blank the defaults will be used.') ?></p>
    184202<?php else : ?>
     
    204222</p>
    205223  </form>
    206 <?php if ( $permalink_structure && !$usingpi && !$writable ) : ?>
    207   <p><?php _e('If your <code>.htaccess</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so these are the mod_rewrite rules you should have in your <code>.htaccess</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all.') ?></p>
     224<?php if ($iis7_permalinks) :
     225    if ( isset($_POST['submit']) && $permalink_structure && ! $usingpi && ! $writable ) : ?>
     226<p><?php _e('If your <code>web.config</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so this is the url rewrite rule you should have in your <code>web.config</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all. Then insert this rule inside of the <code>/&lt;configuration&gt;/&lt;system.webServer&gt;/&lt;rewrite&gt;/&lt;rules&gt;</code> element in <code>web.config</code> file.') ?></p>
     227<form action="options-permalink.php" method="post">
     228<?php wp_nonce_field('update-permalink') ?>
     229    <p><textarea rows="10" class="large-text readonly" name="rules" id="rules" readonly="readonly"><?php echo wp_specialchars($wp_rewrite->iis7_url_rewrite_rules()); ?></textarea></p>
     230</form>
     231<p><?php _e('If you temporarily make your <code>web.config</code> file writable for us to generate rewrite rules automatically, do not forget to revert the permissions after rule has been saved.')  ?></p> 
     232    <?php endif; ?>
     233<?php else :
     234    if ( $permalink_structure && ! $usingpi && ! $writable ) : ?>
     235<p><?php _e('If your <code>.htaccess</code> file were <a href="http://codex.wordpress.org/Changing_File_Permissions">writable</a>, we could do this automatically, but it isn&#8217;t so these are the mod_rewrite rules you should have in your <code>.htaccess</code> file. Click in the field and press <kbd>CTRL + a</kbd> to select all.') ?></p>
    208236<form action="options-permalink.php" method="post">
    209237<?php wp_nonce_field('update-permalink') ?>
    210238    <p><textarea rows="6" class="large-text readonly" name="rules" id="rules" readonly="readonly"><?php echo wp_specialchars($wp_rewrite->mod_rewrite_rules()); ?></textarea></p>
    211239</form>
     240    <?php endif; ?>
    212241<?php endif; ?>
    213242
  • trunk/wp-includes/rewrite.php

    r11335 r11350  
    16961696        return $rules;
    16971697    }
     1698   
     1699    /**
     1700     * Retrieve IIS7 URL Rewrite formatted rewrite rules to write to web.config file.
     1701     *
     1702     * Does not actually write to the web.config file, but creates the rules for
     1703     * the process that will.
     1704     *
     1705     * @since 2.8.0
     1706     * @access public
     1707     *
     1708     * @return string
     1709     */
     1710    function iis7_url_rewrite_rules(){
     1711       
     1712        if ( ! $this->using_permalinks()) {
     1713            return '';
     1714        }
     1715        $rules  = "<rule name=\"wordpress\" patternSyntax=\"Wildcard\">\n";
     1716        $rules .= " <match url=\"*\" />\n";
     1717        $rules .= " <conditions>\n";
     1718        $rules .= "     <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" />\n";
     1719        $rules .= "     <add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" negate=\"true\" />\n";
     1720        $rules .= " </conditions>\n";
     1721        $rules .= " <action type=\"Rewrite\" url=\"index.php\" />\n";
     1722        $rules .= "</rule>";
     1723               
     1724        $rules = apply_filters('iis7_url_rewrite_rules', $rules);
     1725       
     1726        return $rules;
     1727    }
    16981728
    16991729    /**
     
    17911821        if ( function_exists('save_mod_rewrite_rules') )
    17921822            save_mod_rewrite_rules();
     1823        if ( function_exists('iis7_save_url_rewrite_rules') )
     1824            iis7_save_url_rewrite_rules();
    17931825    }
    17941826
  • trunk/wp-includes/vars.php

    r10010 r11350  
    7474$is_IIS = (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false) ? true : false;
    7575
     76/**
     77 * Whether the server software is IIS 7.X
     78 * @global bool $is_IIS7
     79 */
     80$is_iis7 = ($is_IIS && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7.') !== false) ? true : false;
     81
     82
    7683?>
Note: See TracChangeset for help on using the changeset viewer.