Esempio n. 1
0
 /**
  * This file allows the user to search the wiki documentation
  * for a little help.
  */
 public function action_search_doc()
 {
     global $context;
     $context['doc_apiurl'] = 'https://github.com/elkarte/Elkarte/wiki/api.php';
     $context['doc_scripturl'] = 'https://github.com/elkarte/Elkarte/wiki/';
     // Set all the parameters search might expect.
     $postVars = explode(' ', $context['search_term']);
     // Encode the search data.
     foreach ($postVars as $k => $v) {
         $postVars[$k] = urlencode($v);
     }
     // This is what we will send.
     $postVars = implode('+', $postVars);
     // Get the results from the doc site.
     require_once SUBSDIR . '/Package.subs.php';
     // Demo URL:
     // https://github.com/elkarte/Elkarte/wiki/api.php?action=query&list=search&srprop=timestamp|snippet&format=xml&srwhat=text&srsearch=template+eval
     $search_results = fetch_web_data($context['doc_apiurl'] . '?action=query&list=search&srprop=timestamp|snippet&format=xml&srwhat=text&srsearch=' . $postVars);
     // If we didn't get any xml back we are in trouble - perhaps the doc site is overloaded?
     if (!$search_results || preg_match('~<' . '\\?xml\\sversion="\\d+\\.\\d+"\\?>\\s*(<api>.+?</api>)~is', $search_results, $matches) != true) {
         fatal_lang_error('cannot_connect_doc_site');
     }
     $search_results = $matches[1];
     // Otherwise we simply walk through the XML and stick it in context for display.
     $context['search_results'] = array();
     require_once SUBSDIR . '/XmlArray.class.php';
     // Get the results loaded into an array for processing!
     $results = new Xml_Array($search_results, false);
     // Move through the api layer.
     if (!$results->exists('api')) {
         fatal_lang_error('cannot_connect_doc_site');
     }
     // Are there actually some results?
     if ($results->exists('api/query/search/p')) {
         $relevance = 0;
         foreach ($results->set('api/query/search/p') as $result) {
             $context['search_results'][$result->fetch('@title')] = array('title' => $result->fetch('@title'), 'relevance' => $relevance++, 'snippet' => str_replace('class=\'searchmatch\'', 'class="highlight"', un_htmlspecialchars($result->fetch('@snippet'))));
         }
     }
 }
Esempio n. 2
0
/**
 * Parses a xml-style modification file (file).
 *
 * @package Packages
 * @param string $file
 * @param bool $testing = true tells it the modifications shouldn't actually be saved.
 * @param bool $undo = false specifies that the modifications the file requests should be undone; this doesn't work with everything (regular expressions.)
 * @param mixed[] $theme_paths = array()
 * @return array an array of those changes made.
 */
function parseModification($file, $testing = true, $undo = false, $theme_paths = array())
{
    global $txt, $modSettings;
    @set_time_limit(600);
    require_once SUBSDIR . '/XmlArray.class.php';
    $xml = new Xml_Array(strtr($file, array("\r" => '')));
    $actions = array();
    $everything_found = true;
    if (!$xml->exists('modification') || !$xml->exists('modification/file')) {
        $actions[] = array('type' => 'error', 'filename' => '-', 'debug' => $txt['package_modification_malformed']);
        return $actions;
    }
    // Get the XML data.
    $files = $xml->set('modification/file');
    // Use this for holding all the template changes in this mod.
    $template_changes = array();
    // This is needed to hold the long paths, as they can vary...
    $long_changes = array();
    // First, we need to build the list of all the files likely to get changed.
    foreach ($files as $file) {
        // What is the filename we're currently on?
        $filename = parse_path(trim($file->fetch('@name')));
        // Now, we need to work out whether this is even a template file...
        foreach ($theme_paths as $id => $theme) {
            // If this filename is relative, if so take a guess at what it should be.
            $real_filename = $filename;
            if (strpos($filename, 'themes') === 0) {
                $real_filename = BOARDDIR . '/' . $filename;
            }
            if (strpos($real_filename, $theme['theme_dir']) === 0) {
                $template_changes[$id][] = substr($real_filename, strlen($theme['theme_dir']) + 1);
                $long_changes[$id][] = $filename;
            }
        }
    }
    // Custom themes to add.
    $custom_themes_add = array();
    // If we have some template changes, we need to build a master link of what new ones are required for the custom themes.
    if (!empty($template_changes[1])) {
        foreach ($theme_paths as $id => $theme) {
            // Default is getting done anyway, so no need for involvement here.
            if ($id == 1) {
                continue;
            }
            // For every template, do we want it? Yea, no, maybe?
            foreach ($template_changes[1] as $index => $template_file) {
                // What, it exists and we haven't already got it?! Lordy, get it in!
                if (file_exists($theme['theme_dir'] . '/' . $template_file) && (!isset($template_changes[$id]) || !in_array($template_file, $template_changes[$id]))) {
                    // Now let's add it to the "todo" list.
                    $custom_themes_add[$long_changes[1][$index]][$id] = $theme['theme_dir'] . '/' . $template_file;
                }
            }
        }
    }
    foreach ($files as $file) {
        // This is the actual file referred to in the XML document...
        $files_to_change = array(1 => parse_path(trim($file->fetch('@name'))));
        // Sometimes though, we have some additional files for other themes, if we have add them to the mix.
        if (isset($custom_themes_add[$files_to_change[1]])) {
            $files_to_change += $custom_themes_add[$files_to_change[1]];
        }
        // Now, loop through all the files we're changing, and, well, change them ;)
        foreach ($files_to_change as $theme => $working_file) {
            if ($working_file[0] != '/' && $working_file[1] != ':') {
                trigger_error('parseModification(): The filename \'' . $working_file . '\' is not a full path!', E_USER_WARNING);
                $working_file = BOARDDIR . '/' . $working_file;
            }
            // Doesn't exist - give an error or what?
            if (!file_exists($working_file) && (!$file->exists('@error') || !in_array(trim($file->fetch('@error')), array('ignore', 'skip')))) {
                $actions[] = array('type' => 'missing', 'filename' => $working_file, 'debug' => $txt['package_modification_missing']);
                $everything_found = false;
                continue;
            } elseif (!file_exists($working_file) && $file->exists('@error') && trim($file->fetch('@error')) == 'skip') {
                $actions[] = array('type' => 'skipping', 'filename' => $working_file);
                continue;
            } elseif (!file_exists($working_file)) {
                $working_data = '';
            } else {
                $working_data = str_replace("\r", '', package_get_contents($working_file));
            }
            $actions[] = array('type' => 'opened', 'filename' => $working_file);
            $operations = $file->exists('operation') ? $file->set('operation') : array();
            foreach ($operations as $operation) {
                // Convert operation to an array.
                $actual_operation = array('searches' => array(), 'error' => $operation->exists('@error') && in_array(trim($operation->fetch('@error')), array('ignore', 'fatal', 'required')) ? trim($operation->fetch('@error')) : 'fatal');
                // The 'add' parameter is used for all searches in this operation.
                $add = $operation->exists('add') ? $operation->fetch('add') : '';
                // Grab all search items of this operation (in most cases just 1).
                $searches = $operation->set('search');
                foreach ($searches as $i => $search) {
                    $actual_operation['searches'][] = array('position' => $search->exists('@position') && in_array(trim($search->fetch('@position')), array('before', 'after', 'replace', 'end')) ? trim($search->fetch('@position')) : 'replace', 'is_reg_exp' => $search->exists('@regexp') && trim($search->fetch('@regexp')) === 'true', 'loose_whitespace' => $search->exists('@whitespace') && trim($search->fetch('@whitespace')) === 'loose', 'search' => $search->fetch('.'), 'add' => $add, 'preg_search' => '', 'preg_replace' => '');
                }
                // At least one search should be defined.
                if (empty($actual_operation['searches'])) {
                    $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search'], 'is_custom' => $theme > 1 ? $theme : 0);
                    // Skip to the next operation.
                    continue;
                }
                // Reverse the operations in case of undoing stuff.
                if ($undo) {
                    foreach ($actual_operation['searches'] as $i => $search) {
                        // Reverse modification of regular expressions are not allowed.
                        if ($search['is_reg_exp']) {
                            if ($actual_operation['error'] === 'fatal') {
                                $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search'], 'is_custom' => $theme > 1 ? $theme : 0);
                            }
                            // Continue to the next operation.
                            continue 2;
                        }
                        // The replacement is now the search subject...
                        if ($search['position'] === 'replace' || $search['position'] === 'end') {
                            $actual_operation['searches'][$i]['search'] = $search['add'];
                        } else {
                            // Reversing a before/after modification becomes a replacement.
                            $actual_operation['searches'][$i]['position'] = 'replace';
                            if ($search['position'] === 'before') {
                                $actual_operation['searches'][$i]['search'] .= $search['add'];
                            } elseif ($search['position'] === 'after') {
                                $actual_operation['searches'][$i]['search'] = $search['add'] . $search['search'];
                            }
                        }
                        // ...and the search subject is now the replacement.
                        $actual_operation['searches'][$i]['add'] = $search['search'];
                    }
                }
                // Sort the search list so the replaces come before the add before/after's.
                if (count($actual_operation['searches']) !== 1) {
                    $replacements = array();
                    foreach ($actual_operation['searches'] as $i => $search) {
                        if ($search['position'] === 'replace') {
                            $replacements[] = $search;
                            unset($actual_operation['searches'][$i]);
                        }
                    }
                    $actual_operation['searches'] = array_merge($replacements, $actual_operation['searches']);
                }
                // Create regular expression replacements from each search.
                foreach ($actual_operation['searches'] as $i => $search) {
                    // Not much needed if the search subject is already a regexp.
                    if ($search['is_reg_exp']) {
                        $actual_operation['searches'][$i]['preg_search'] = $search['search'];
                    } else {
                        // Make the search subject fit into a regular expression.
                        $actual_operation['searches'][$i]['preg_search'] = preg_quote($search['search'], '~');
                        // Using 'loose', a random amount of tabs and spaces may be used.
                        if ($search['loose_whitespace']) {
                            $actual_operation['searches'][$i]['preg_search'] = preg_replace('~[ \\t]+~', '[ \\t]+', $actual_operation['searches'][$i]['preg_search']);
                        }
                    }
                    // Shuzzup.  This is done so we can safely use a regular expression. ($0 is bad!!)
                    $actual_operation['searches'][$i]['preg_replace'] = strtr($search['add'], array('$' => '[$PACK' . 'AGE1$]', '\\' => '[$PACK' . 'AGE2$]'));
                    // Before, so the replacement comes after the search subject :P
                    if ($search['position'] === 'before') {
                        $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                        $actual_operation['searches'][$i]['preg_replace'] = '$1' . $actual_operation['searches'][$i]['preg_replace'];
                    } elseif ($search['position'] === 'after') {
                        $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                        $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                    } elseif ($search['position'] === 'end') {
                        if ($undo) {
                            $actual_operation['searches'][$i]['preg_replace'] = '';
                        } else {
                            $actual_operation['searches'][$i]['preg_search'] = '(\\n\\?\\>)?$';
                            $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                        }
                    }
                    // Testing 1, 2, 3...
                    $failed = preg_match('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $working_data) === 0;
                    // Nope, search pattern not found.
                    if ($failed && $actual_operation['error'] === 'fatal') {
                        $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'is_custom' => $theme > 1 ? $theme : 0, 'failed' => $failed);
                        $everything_found = false;
                        continue;
                    } elseif (!$failed && $actual_operation['error'] === 'required') {
                        $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'is_custom' => $theme > 1 ? $theme : 0, 'failed' => $failed);
                        $everything_found = false;
                        continue;
                    }
                    // Replace it into nothing? That's not an option...unless it's an undoing end.
                    if ($search['add'] === '' && ($search['position'] !== 'end' || !$undo)) {
                        continue;
                    }
                    // Finally, we're doing some replacements.
                    $working_data = preg_replace('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $actual_operation['searches'][$i]['preg_replace'], $working_data, 1);
                    $actions[] = array('type' => 'replace', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'replace' => $actual_operation['searches'][$i]['preg_replace'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'failed' => $failed, 'ignore_failure' => $failed && $actual_operation['error'] === 'ignore', 'is_custom' => $theme > 1 ? $theme : 0);
                }
            }
            // Fix any little helper symbols ;).
            $working_data = strtr($working_data, array('[$PACK' . 'AGE1$]' => '$', '[$PACK' . 'AGE2$]' => '\\'));
            package_chmod($working_file);
            if (file_exists($working_file) && !is_writable($working_file) || !file_exists($working_file) && !is_writable(dirname($working_file))) {
                $actions[] = array('type' => 'chmod', 'filename' => $working_file);
            }
            if (basename($working_file) == 'Settings_bak.php') {
                continue;
            }
            if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) {
                // No, no, not Settings.php!
                if (basename($working_file) == 'Settings.php') {
                    @copy($working_file, dirname($working_file) . '/Settings_bak.php');
                } else {
                    @copy($working_file, $working_file . '~');
                }
            }
            // Always call this, even if in testing, because it won't really be written in testing mode.
            package_put_contents($working_file, $working_data, $testing);
            $actions[] = array('type' => 'saved', 'filename' => $working_file, 'is_custom' => $theme > 1 ? $theme : 0);
        }
    }
    $actions[] = array('type' => 'result', 'status' => $everything_found);
    return $actions;
}