|
- <?php
- /**
- * Move Plugin Operation Planner
- *
- * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
- * @author Michael Hamann <michael@content-space.de>
- * @author Andreas Gohr <gohr@cosmocode.de>
- */
- // must be run within Dokuwiki
- if(!defined('DOKU_INC')) die();
-
- /**
- * Class helper_plugin_move_plan
- *
- * This thing prepares and keeps progress info on complex move operations (eg. where more than a single
- * object is affected).
- *
- * Please note: this has not a complex move resolver. Move operations may not depend on each other (eg. you
- * can not use a namespace as source that will only be created by a different move operation) instead all given
- * operations should be operations on the current state to come to a wanted future state. The tree manager takes
- * care of that by abstracting all moves on a DOM representation first, then submitting the needed changes (eg.
- * differences between now and wanted).
- *
- * Glossary:
- *
- * document - refers to either a page or a media file here
- */
- class helper_plugin_move_plan extends DokuWiki_Plugin {
- /** Number of operations per step */
- const OPS_PER_RUN = 10;
-
- const TYPE_PAGES = 1;
- const TYPE_MEDIA = 2;
- const CLASS_NS = 4;
- const CLASS_DOC = 8;
-
- /**
- * @var array the options for this move plan
- */
- protected $options = array(); // defaults are set in loadOptions()
-
- /**
- * @var array holds the location of the different list and state files
- */
- protected $files = array();
-
- /**
- * @var array the planned moves
- */
- protected $plan = array();
-
- /**
- * @var array temporary holder of document lists
- */
- protected $tmpstore = array(
- 'pages' => array(),
- 'media' => array(),
- 'ns' => array(),
- 'affpg' => array(),
- 'miss' => array(),
- 'miss_media' => array(),
- );
-
- /** @var helper_plugin_move_op $MoveOperator */
- protected $MoveOperator = null;
-
- /**
- * Constructor
- *
- * initializes state (if any) for continuiation of a running move op
- */
- public function __construct() {
- global $conf;
-
- // set the file locations
- $this->files = array(
- 'opts' => $conf['metadir'] . '/__move_opts',
- 'pagelist' => $conf['metadir'] . '/__move_pagelist',
- 'medialist' => $conf['metadir'] . '/__move_medialist',
- 'affected' => $conf['metadir'] . '/__move_affected',
- 'namespaces' => $conf['metadir'] . '/__move_namespaces',
- 'missing' => $conf['metadir'] . '/__move_missing',
- 'missing_media' => $conf['metadir'] . '/__move_missing_media',
- );
-
- $this->MoveOperator = plugin_load('helper', 'move_op');
-
- $this->loadOptions();
- }
-
- /**
- * Load the current options if any
- *
- * If no options are found, the default options will be extended by any available
- * config options
- */
- protected function loadOptions() {
- // (re)set defaults
- $this->options = array(
- // status
- 'committed' => false,
- 'started' => 0,
-
- // counters
- 'pages_all' => 0,
- 'pages_run' => 0,
- 'media_all' => 0,
- 'media_run' => 0,
- 'affpg_all' => 0,
- 'affpg_run' => 0,
-
- // options
- 'autoskip' => $this->getConf('autoskip'),
- 'autorewrite' => $this->getConf('autorewrite'),
-
- // errors
- 'lasterror' => false
- );
-
- // merge whatever options are saved currently
- $file = $this->files['opts'];
- if(file_exists($file)) {
- $options = unserialize(io_readFile($file, false));
- $this->options = array_merge($this->options, $options);
- }
- }
-
- /**
- * Save the current options
- *
- * @return bool
- */
- protected function saveOptions() {
- return io_saveFile($this->files['opts'], serialize($this->options));
- }
-
- /**
- * Return the current state of an option, null for unknown options
- *
- * @param $name
- * @return mixed|null
- */
- public function getOption($name) {
- if(isset($this->options[$name])) {
- return $this->options[$name];
- }
- return null;
- }
-
- /**
- * Set an option
- *
- * Note, this otpion will only be set to the current instance of this helper object. It will only
- * be written to the option file once the plan gets committed
- *
- * @param $name
- * @param $value
- */
- public function setOption($name, $value) {
- $this->options[$name] = $value;
- }
-
- /**
- * Returns the progress of this plan in percent
- *
- * @return float
- */
- public function getProgress() {
- $max =
- $this->options['pages_all'] +
- $this->options['media_all'];
-
- $remain =
- $this->options['pages_run'] +
- $this->options['media_run'];
-
- if($this->options['autorewrite']) {
- $max += $this->options['affpg_all'];
- $remain += $this->options['affpg_run'];
- }
-
- if($max == 0) return 0;
- return round((($max - $remain) * 100) / $max, 2);
- }
-
- /**
- * Check if there is a move in progress currently
- *
- * @return bool
- */
- public function inProgress() {
- return (bool) $this->options['started'];
- }
-
- /**
- * Check if this plan has been committed, yet
- *
- * @return bool
- */
- public function isCommited() {
- return $this->options['committed'];
- }
-
- /**
- * Add a single page to be moved to the plan
- *
- * @param string $src
- * @param string $dst
- */
- public function addPageMove($src, $dst) {
- $this->addMove($src, $dst, self::CLASS_DOC, self::TYPE_PAGES);
- }
-
- /**
- * Add a single media file to be moved to the plan
- *
- * @param string $src
- * @param string $dst
- */
- public function addMediaMove($src, $dst) {
- $this->addMove($src, $dst, self::CLASS_DOC, self::TYPE_MEDIA);
- }
-
- /**
- * Add a page namespace to be moved to the plan
- *
- * @param string $src
- * @param string $dst
- */
- public function addPageNamespaceMove($src, $dst) {
- $this->addMove($src, $dst, self::CLASS_NS, self::TYPE_PAGES);
- }
-
- /**
- * Add a media namespace to be moved to the plan
- *
- * @param string $src
- * @param string $dst
- */
- public function addMediaNamespaceMove($src, $dst) {
- $this->addMove($src, $dst, self::CLASS_NS, self::TYPE_MEDIA);
- }
-
- /**
- * Plans the move of a namespace or document
- *
- * @param string $src ID of the item to move
- * @param string $dst new ID of item namespace
- * @param int $class (self::CLASS_NS|self::CLASS_DOC)
- * @param int $type (PLUGIN_MOVE_TYPE_PAGE|self::TYPE_MEDIA)
- * @throws Exception
- */
- protected function addMove($src, $dst, $class = self::CLASS_NS, $type = self::TYPE_PAGES) {
- if($this->options['committed']) throw new Exception('plan is committed already, can not be added to');
-
- $src = cleanID($src);
- $dst = cleanID($dst);
-
- $this->plan[] = array(
- 'src' => $src,
- 'dst' => $dst,
- 'class' => $class,
- 'type' => $type
- );
- }
-
- /**
- * Abort any move or plan in progress and reset the helper
- */
- public function abort() {
- foreach($this->files as $file) {
- @unlink($file);
- }
- $this->plan = array();
- $this->loadOptions();
- helper_plugin_move_rewrite::removeAllLocks();
- }
-
- /**
- * This locks up the plan and prepares execution
- *
- * the plan is reordered an the needed move operations are gathered and stored in the appropriate
- * list files
- *
- * @throws Exception if you try to commit a plan twice
- * @return bool true if the plan was committed
- */
- public function commit() {
- global $conf;
-
- if($this->options['committed']) throw new Exception('plan is committed already, can not be committed again');
-
- helper_plugin_move_rewrite::addLock();
-
-
- usort($this->plan, array($this, 'planSorter'));
-
- // get all the documents to be moved and store them in their lists
- foreach($this->plan as $move) {
- if($move['class'] == self::CLASS_DOC) {
- // these can just be added
- $this->addToDocumentList($move['src'], $move['dst'], $move['type']);
- } else {
- // here we need a list of content first, search for it
- $docs = array();
- $path = utf8_encodeFN(str_replace(':', '/', $move['src']));
- $opts = array('depth' => 0, 'skipacl' => true);
- if($move['type'] == self::TYPE_PAGES) {
- search($docs, $conf['datadir'], 'search_allpages', $opts, $path);
- } else {
- search($docs, $conf['mediadir'], 'search_media', $opts, $path);
- }
-
- // how much namespace to strip?
- if($move['src'] !== '') {
- $strip = strlen($move['src']) + 1;
- } else {
- $strip = 0;
- }
- if($move['dst']) $move['dst'] .= ':';
-
- // now add all the found documents to our lists
- foreach($docs as $doc) {
- $from = $doc['id'];
- $to = $move['dst'] . substr($doc['id'], $strip);
- $this->addToDocumentList($from, $to, $move['type']);
- }
-
- // remember the namespace move itself
- if($move['type'] == self::TYPE_PAGES) {
- // FIXME we use this to move namespace subscriptions later on and for now only do it on
- // page namespace moves, but subscriptions work for both, but what when only one of
- // them is moved? Should it be copied then? Complicated. This is good enough for now
- $this->addToDocumentList($move['src'], $move['dst'], self::CLASS_NS);
- }
- $this->findMissingDocuments($move['src'] . ':', $move['dst'],$move['type']);
- }
- // store what pages are affected by this move
- $this->findAffectedPages($move['src'], $move['dst'], $move['class'], $move['type']);
- }
-
- $this->storeDocumentLists();
-
- if(!$this->options['pages_all'] && !$this->options['media_all']) {
- msg($this->getLang('noaction'), -1);
- return false;
- }
-
- $this->options['committed'] = true;
- $this->saveOptions();
-
- return true;
- }
-
- /**
- * Execute the next steps
- *
- * @param bool $skip set to true to skip the next first step (skip error)
- * @return bool|int false on errors, otherwise the number of remaining steps
- * @throws Exception
- */
- public function nextStep($skip = false) {
- if(!$this->options['committed']) throw new Exception('plan is not committed yet!');
-
- // execution has started
- if(!$this->options['started']) $this->options['started'] = time();
-
- helper_plugin_move_rewrite::addLock();
-
- if(@filesize($this->files['pagelist']) > 1) {
- $todo = $this->stepThroughDocuments(self::TYPE_PAGES, $skip);
- if($todo === false) return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- if(@filesize($this->files['medialist']) > 1) {
- $todo = $this->stepThroughDocuments(self::TYPE_MEDIA, $skip);
- if($todo === false) return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- if(@filesize($this->files['missing']) > 1 && @filesize($this->files['affected']) > 1) {
- $todo = $this->stepThroughMissingDocuments(self::TYPE_PAGES);
- if($todo === false) return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- if(@filesize($this->files['missing_media']) > 1 && @filesize($this->files['affected']) > 1) {
- $todo = $this->stepThroughMissingDocuments(self::TYPE_MEDIA);
- if($todo === false)return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- if(@filesize($this->files['namespaces']) > 1) {
- $todo = $this->stepThroughNamespaces();
- if($todo === false) return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- helper_plugin_move_rewrite::removeAllLocks();
-
- if($this->options['autorewrite'] && @filesize($this->files['affected']) > 1) {
- $todo = $this->stepThroughAffectedPages();
- if($todo === false) return $this->storeError();
- return max($todo, 1); // force one more call
- }
-
- // we're done here, clean up
- $this->abort();
- return 0;
- }
-
- /**
- * Returns the list of page and media moves and the affected pages as a HTML list
- *
- * @return string
- */
- public function previewHTML() {
- $html = '';
-
- $html .= '<ul>';
- if(@file_exists($this->files['pagelist'])) {
- $pagelist = file($this->files['pagelist']);
- foreach($pagelist as $line) {
- list($old, $new) = explode("\t", trim($line));
-
- $html .= '<li class="page"><div class="li">';
- $html .= hsc($old);
- $html .= '→';
- $html .= hsc($new);
- $html .= '</div></li>';
- }
- }
- if(@file_exists($this->files['medialist'])) {
- $medialist = file($this->files['medialist']);
- foreach($medialist as $line) {
- list($old, $new) = explode("\t", trim($line));
-
- $html .= '<li class="media"><div class="li">';
- $html .= hsc($old);
- $html .= '→';
- $html .= hsc($new);
- $html .= '</div></li>';
- }
- }
- if(@file_exists($this->files['affected'])) {
- $medialist = file($this->files['affected']);
- foreach($medialist as $page) {
- $html .= '<li class="affected"><div class="li">';
- $html .= '↷';
- $html .= hsc($page);
- $html .= '</div></li>';
- }
- }
- $html .= '</ul>';
-
- return $html;
- }
-
- /**
- * Step through the next bunch of pages or media files
- *
- * @param int $type (self::TYPE_PAGES|self::TYPE_MEDIA)
- * @param bool $skip should the first item be skipped?
- * @return bool|int false on error, otherwise the number of remaining documents
- */
- protected function stepThroughDocuments($type = self::TYPE_PAGES, $skip = false) {
-
- if($type == self::TYPE_PAGES) {
- $file = $this->files['pagelist'];
- $mark = 'P';
- $call = 'movePage';
- $items_run_counter = 'pages_run';
- } else {
- $file = $this->files['medialist'];
- $mark = 'M';
- $call = 'moveMedia';
- $items_run_counter = 'media_run';
- }
-
- $doclist = fopen($file, 'a+');
-
- for($i = 0; $i < helper_plugin_move_plan::OPS_PER_RUN; $i++) {
- $log = "";
- $line = $this->getLastLine($doclist);
- if($line === false) {
- break;
- }
- list($src, $dst) = explode("\t", trim($line));
-
- // should this item be skipped?
- if($skip === true) {
- $skip = false;
- } else {
- // move the page
- if(!$this->MoveOperator->$call($src, $dst)) {
- $log .= $this->build_log_line($mark, $src, $dst, false); // FAILURE!
-
- // automatically skip this item only if wanted...
- if(!$this->options['autoskip']) {
- // ...otherwise abort the operation
- fclose($doclist);
- $return_items_run = false;
- break;
- }
- } else {
- $log .= $this->build_log_line($mark, $src, $dst, true); // SUCCESS!
- }
- }
-
- /*
- * This adjusts counters and truncates the document list correctly
- * It is used to finalize a successful or skipped move
- */
-
- ftruncate($doclist, ftell($doclist));
- $this->options[$items_run_counter]--;
- $return_items_run = $this->options[$items_run_counter];
- $this->write_log($log);
- $this->saveOptions();
- }
-
- if ($return_items_run !== false) {
- fclose($doclist);
- }
- return $return_items_run;
- }
-
- /**
- * Step through the next bunch of pages that need link corrections
- *
- * @return bool|int false on error, otherwise the number of remaining documents
- */
- protected function stepThroughAffectedPages() {
- /** @var helper_plugin_move_rewrite $Rewriter */
- $Rewriter = plugin_load('helper', 'move_rewrite');
-
- // handle affected pages
- $doclist = fopen($this->files['affected'], 'a+');
- for($i = 0; $i < helper_plugin_move_plan::OPS_PER_RUN; $i++) {
- $page = $this->getLastLine($doclist);
- if($page === false) break;
-
- // rewrite it
- $Rewriter->rewritePage($page);
-
- // update the list file
- ftruncate($doclist, ftell($doclist));
- $this->options['affpg_run']--;
- $this->saveOptions();
- }
-
- fclose($doclist);
- return $this->options['affpg_run'];
- }
-
- /**
- * Step through all the links to missing pages that should be moved
- *
- * This simply adds the moved missing pages to all affected pages meta data. This will add
- * the meta data to pages not linking to the affected pages but this should still be faster
- * than figuring out which pages need this info.
- *
- * This does not step currently, but handles all pages in one step.
- *
- * @param int $type
- *
- * @return int always 0
- * @throws Exception
- */
- protected function stepThroughMissingDocuments($type = self::TYPE_PAGES) {
- if($type != self::TYPE_PAGES && $type != self::TYPE_MEDIA) {
- throw new Exception('wrong type specified');
- }
- /** @var helper_plugin_move_rewrite $Rewriter */
- $Rewriter = plugin_load('helper', 'move_rewrite');
-
- $miss = array();
- if ($type == self::TYPE_PAGES) {
- $missing_fn = $this->files['missing'];
- } else {
- $missing_fn = $this->files['missing_media'];
- }
- $missing = file($missing_fn);
- foreach($missing as $line) {
- $line = trim($line);
- if($line == '') continue;
- list($src, $dst) = explode("\t", $line);
- $miss[$src] = $dst;
- }
-
- $affected = file($this->files['affected']);
- foreach($affected as $page){
- $page = trim($page);
-
- if ($type == self::TYPE_PAGES) {
- $Rewriter->setMoveMetas($page, $miss, 'pages');
- } else {
- $Rewriter->setMoveMetas($page, $miss, 'media');
- }
- }
-
- unlink($missing_fn);
- return 0;
- }
-
- /**
- * Step through all the namespace moves
- *
- * This does not step currently, but handles all namespaces in one step.
- *
- * Currently moves namespace subscriptions only.
- *
- * @return int always 0
- * @todo maybe add an event so plugins can move more stuff?
- * @todo fixed that $src and $dst are seperated by tab, not newline. This method has no tests?
- */
- protected function stepThroughNamespaces() {
- /** @var helper_plugin_move_file $FileMover */
- $FileMover = plugin_load('helper', 'move_file');
-
- $lines = io_readFile($this->files['namespaces']);
- $lines = explode("\n", $lines);
-
- foreach($lines as $line) {
- // There is an empty line at the end of the list.
- if ($line === '') continue;
-
- list($src, $dst) = explode("\t", trim($line));
- $FileMover->moveNamespaceSubscription($src, $dst);
- }
-
- @unlink($this->files['namespaces']);
- return 0;
- }
-
- /**
- * Retrieve the last error from the MSG array and store it in the options
- *
- * @todo rebuild error handling based on exceptions
- *
- * @return bool always false
- */
- protected function storeError() {
- global $MSG;
-
- if(is_array($MSG) && count($MSG)) {
- $last = array_shift($MSG);
- $this->options['lasterror'] = $last['msg'];
- unset($GLOBALS['MSG']);
- } else {
- $this->options['lasterror'] = 'Unknown error';
- }
- $this->saveOptions();
-
- return false;
- }
-
- /**
- * Reset the error state
- */
- protected function clearError() {
- $this->options['lasterror'] = false;
- $this->saveOptions();
- }
-
- /**
- * Get the last error message or false if no error occured
- *
- * @return bool|string
- */
- public function getLastError() {
- return $this->options['lasterror'];
- }
-
- /**
- * Appends a page move operation in the list file
- *
- * If the src has been added before, this is ignored. This makes sure you can move a single page
- * out of a namespace first, then move the namespace somewhere else.
- *
- * @param string $src
- * @param string $dst
- * @param int $type
- * @throws Exception
- */
- protected function addToDocumentList($src, $dst, $type = self::TYPE_PAGES) {
- if($type == self::TYPE_PAGES) {
- $store = 'pages';
- } else if($type == self::TYPE_MEDIA) {
- $store = 'media';
- } else if($type == self::CLASS_NS) {
- $store = 'ns';
- } else {
- throw new Exception('Unknown type ' . $type);
- }
-
- if(!isset($this->tmpstore[$store][$src])) {
- $this->tmpstore[$store][$src] = $dst;
- }
- }
-
- /**
- * Add the list of pages to the list of affected pages whose links need adjustment
- *
- * @param string|array $pages
- */
- protected function addToAffectedPagesList($pages) {
- if(!is_array($pages)) $pages = array($pages);
-
- foreach($pages as $page) {
- if(!isset($this->tmpstore['affpg'][$page])) {
- $this->tmpstore['affpg'][$page] = true;
- }
- }
- }
-
- /**
- * Looks up pages that will be affected by a move of $src
- *
- * Calls addToAffectedPagesList() directly to store the result
- *
- * @param string $src source namespace
- * @param string $dst destination namespace
- * @param int $class
- * @param int $type
- */
- protected function findAffectedPages($src, $dst, $class, $type) {
- $idx = idx_get_indexer();
-
- if($class == self::CLASS_NS) {
- $src_ = "$src:*"; // use wildcard lookup for namespaces
- } else {
- $src_ = $src;
- }
-
- $pages = array();
- if($type == self::TYPE_PAGES) {
- $pages = $idx->lookupKey('relation_references', $src_);
- $len = strlen($src);
- foreach($pages as &$page) {
- if (substr($page, 0, $len + 1) === "$src:") {
- $page = $dst . substr($page, $len + 1);
- }
- }
- unset($page);
- } else if($type == self::TYPE_MEDIA) {
- $pages = $idx->lookupKey('relation_media', $src_);
- }
-
- $this->addToAffectedPagesList($pages);
- }
-
- /**
- * Find missing pages in the $src namespace
- *
- * @param string $src source namespace
- * @param string $dst destination namespace
- * @param int $type either self::TYPE_PAGES or self::TYPE_MEDIA
- */
- protected function findMissingDocuments($src, $dst, $type = self::TYPE_PAGES) {
- global $conf;
-
- // FIXME this duplicates Doku_Indexer::getIndex()
- if ($type == self::TYPE_PAGES) {
- $fn = $conf['indexdir'] . '/relation_references_w.idx';
- } else {
- $fn = $conf['indexdir'] . '/relation_media_w.idx';
- }
- if (!@file_exists($fn)){
- $referenceidx = array();
- } else {
- $referenceidx = file($fn, FILE_IGNORE_NEW_LINES);
- }
-
- $len = strlen($src);
- foreach($referenceidx as $idx => $page) {
- if(substr($page, 0, $len) != "$src") continue;
-
- // remember missing pages
- if ($type == self::TYPE_PAGES) {
- if(!page_exists($page)) {
- $newpage = $dst . substr($page, $len);
- $this->tmpstore['miss'][$page] = $newpage;
- }
- } else {
- if(!file_exists(mediaFN($page))){
- $newpage = $dst . substr($page, $len);
- $this->tmpstore['miss_media'][$page] = $newpage;
- }
- }
- }
- }
-
- /**
- * Store the aggregated document lists in the file system and reset the internal storage
- *
- * @throws Exception
- */
- protected function storeDocumentLists() {
- $lists = array(
- 'pages' => $this->files['pagelist'],
- 'media' => $this->files['medialist'],
- 'ns' => $this->files['namespaces'],
- 'affpg' => $this->files['affected'],
- 'miss' => $this->files['missing'],
- 'miss_media' => $this->files['missing_media'],
- );
-
- foreach($lists as $store => $file) {
- // anything to do?
- $count = count($this->tmpstore[$store]);
- if(!$count) continue;
-
- // prepare and save content
- $data = '';
- $this->tmpstore[$store] = array_reverse($this->tmpstore[$store]); // store in reverse order
- foreach($this->tmpstore[$store] as $src => $dst) {
- if($dst === true) {
- $data .= "$src\n"; // for affected pages only one ID is saved
- } else {
- $data .= "$src\t$dst\n";
- }
-
- }
- io_saveFile($file, $data);
-
- // set counters
- if($store != 'ns') {
- $this->options[$store . '_all'] = $count;
- $this->options[$store . '_run'] = $count;
- }
-
- // reset the list
- $this->tmpstore[$store] = array();
- }
- }
-
- /**
- * Get the last line from the list that is stored in the file that is referenced by the handle
- * The handle is set to the newline before the file id
- *
- * @param resource $handle The file handle to read from
- * @return string|bool the last id from the list or false if there is none
- */
- protected function getLastLine($handle) {
- // begin the seek at the end of the file
- fseek($handle, 0, SEEK_END);
- $line = '';
-
- // seek one backwards as long as it's possible
- while(fseek($handle, -1, SEEK_CUR) >= 0) {
- $c = fgetc($handle);
- if($c === false) return false; // EOF, i.e. the file is empty
- fseek($handle, -1, SEEK_CUR); // reset the position to the character that was read
-
- if($c == "\n") {
- if($line === '') {
- continue; // this line was empty, continue
- } else {
- break; // we have a line, finish
- }
- }
-
- $line = $c . $line; // prepend char to line
- }
-
- if($line === '') return false; // beginning of file reached and no content
-
- return $line;
- }
-
- /**
- * Callback for usort to sort the move plan
- *
- * @param $a
- * @param $b
- * @return int
- */
- public function planSorter($a, $b) {
- // do page moves before namespace moves
- if($a['class'] == self::CLASS_DOC && $b['class'] == self::CLASS_NS) {
- return -1;
- }
- if($a['class'] == self::CLASS_NS && $b['class'] == self::CLASS_DOC) {
- return 1;
- }
-
- // do pages before media
- if($a['type'] == self::TYPE_PAGES && $b['type'] == self::TYPE_MEDIA) {
- return -1;
- }
- if($a['type'] == self::TYPE_MEDIA && $b['type'] == self::TYPE_PAGES) {
- return 1;
- }
-
- // from here on we compare only apples to apples
- // we sort by depth of namespace, deepest namespaces first
-
- $alen = substr_count($a['src'], ':');
- $blen = substr_count($b['src'], ':');
-
- if($alen > $blen) {
- return -1;
- } elseif($alen < $blen) {
- return 1;
- }
- return 0;
- }
-
- /**
- * Create line to log result of an operation
- *
- * @param string $type
- * @param string $from
- * @param string $to
- * @param bool $success
- *
- * @return string
- *
- * @author Andreas Gohr <gohr@cosmocode.de>
- * @author Michael Große <grosse@cosmocode.de>
- */
- public function build_log_line ($type, $from, $to, $success) {
- global $MSG;
-
- $now = time();
- $date = date('Y-m-d H:i:s', $now); // for human readability
- if($success) {
- $ok = 'success';
- $msg = '';
- } else {
- $ok = 'failed';
- $msg = $MSG[count($MSG) - 1]['msg']; // get detail from message array
- }
-
- $log = "$now\t$date\t$type\t$from\t$to\t$ok\t$msg\n";
- return $log;
- }
-
- /**
- * write log to file
- *
- * @param $log
- */
- protected function write_log ($log) {
- global $conf;
- $optime = $this->options['started'];
- $file = $conf['cachedir'] . '/move/' . strftime('%Y%m%d-%H%M%S', $optime) . '.log';
- io_saveFile($file, $log, true);
- }
-
- }
|