eZ Publish  [4.0]
fixezurlobjectlinks.php
Go to the documentation of this file.
00001 #!/usr/bin/env php
00002 <?php
00003 /**
00004  * File containing the upgrade script to fix older occurrences of link items
00005  * not being present in the ezurl_object_table for all versions/translations.
00006  *
00007  * @copyright Copyright (C) 1999-2008 eZ Systems AS. All rights reserved.
00008  * @license http://ez.no/licenses/gnu_gpl GNU GPLv2
00009  *
00010  */
00011 
00012 require 'autoload.php';
00013 
00014 set_time_limit( 0 );
00015 
00016 $cli = eZCLI::instance();
00017 $script = eZScript::instance( array( 'description' => ( "Fix older occurrences of link items in XML-blocks, where they might not be\n" .
00018                                                         "linked correctly for all versions/translations. This script will update\n" .
00019                                                         "these references for existing entries. Note that URLs which have been lost\n" .
00020                                                         "already will not be restored in this process, these need to be re-entered.\n" .
00021                                                         "\n" .
00022                                                         "fixezurlobjectlink.php" ),
00023                                       'use-session' => false,
00024                                       'use-modules' => true,
00025                                       'use-extensions' => true,
00026                                     ) );
00027 
00028 
00029 $config = "[fix][fetch-limit:]";
00030 $argConfig = "";
00031 $optionHelp = array(
00032                     "fix" => "Fix missing ezurl-object-link references. This will make sure that URLs\n" .
00033                     "created with older versions, will not be lost, when older\nversions/translations are removed.",
00034                     "fetch-limit" => "The number of attributes to fetch in one chunk. Default value is 200,\nthe limit must be higher than 1."
00035                     );
00036 $arguments = false;
00037 $useStandardOptions = true;
00038 
00039 $script->startup();
00040 $options = $script->getOptions( $config, $argConfig, $optionHelp, $arguments, $useStandardOptions );
00041 $script->initialize();
00042 $script->setIterationData( '.', '+' );
00043 
00044 $linkUpdate = new ezpUrlObjectLinkUpdate( $cli, $script, $options );
00045 
00046 $cli->output( $cli->stylize( 'red', "Found ") . $cli->stylize( 'green', $linkUpdate->xmlTextContentObjectAttributeCount() ) . $cli->stylize( 'red', " occurrences of 'ezxmltext'." ) );
00047 
00048 $cli->output();
00049 $cli->output( "Starting to process content object attributes." );
00050 $cli->output( "Fetch limit: " . $cli->stylize( 'green', $linkUpdate->fetchLimit ) );
00051 $cli->output();
00052 $linkUpdate->processData();
00053 
00054 $cli->output();
00055 $cli->output();
00056 $cli->output( $cli->stylize( 'red', "Detailed script summary:" ) );
00057 $cli->output();
00058 $linkUpdate->showSummary();
00059 
00060 $cli->output();
00061 $script->shutdown();
00062 
00063 /**
00064  * Utility class to help fix occurrences of external http-links not correctly
00065  * associated with all versions/translations of an object
00066  *
00067  */
00068 class ezpUrlObjectLinkUpdate
00069 {
00070     public $verboseLevel;
00071     public $xmlClassAttributeIdArray = null;
00072     public $xmlAttrCount = null;
00073 
00074     public $offset;
00075     public $fetchLimit;
00076     public $processedCount;
00077 
00078     public $outputEntryNumber;
00079     public $finalOutputMessageArray;
00080 
00081     public $cli;
00082     public $script;
00083 
00084     public $doFix;
00085 
00086     /**
00087      * Create a new instance of the ezpUrlObjectLink object.
00088      *
00089      * @param eZCLI $cli
00090      * @param eZScript $script
00091      * @param array $options
00092      */
00093     public function __construct( $cli, $script, $options )
00094     {
00095         $this->cli = $cli;
00096         $this->script = $script;
00097 
00098         $this->offset = 0;
00099         $this->fetchLimit = 200;
00100         $this->processedCount = 0;
00101 
00102         $this->verboseLevel = $this->script->verboseOutputLevel();
00103 
00104         $this->script->resetIteration( $this->xmlTextContentObjectAttributeCount() );
00105 
00106         $this->doFix = false;
00107         if ( $options['fix'] !== null and $options['fix'] )
00108         {
00109             $this->doFix = true;
00110         }
00111 
00112         if ($options['fetch-limit'] !== null and $options['fetch-limit'] > 1 )
00113         {
00114             $this->fetchLimit = $options['fetch-limit'];
00115         }
00116     }
00117 
00118     /**
00119      * Get an array of all defined class attributes with ezxmltext datatype.
00120      *
00121      * @return array
00122      */
00123     public function xmlClassAttributeIds()
00124     {
00125         if ( $this->xmlClassAttributeIdArray === null )
00126         {
00127             // First we want to find all class attributes which are defined. We won't be touching
00128             // attributes which are in a transient state.
00129             $xmlTextAttributes = eZContentClassAttribute::fetchList( true, array( 'data_type' => 'ezxmltext',
00130                                                                                   'version' => 0 ) );
00131             $this->xmlClassAttributeIdArray = array();
00132 
00133             foreach ( $xmlTextAttributes as $classAttr )
00134             {
00135                 $this->xmlClassAttributeIdArray[] = $classAttr->attribute( 'id' );
00136             }
00137             unset( $xmlTextAttributes );
00138         }
00139         return $this->xmlClassAttributeIdArray;
00140     }
00141 
00142     /**
00143      * Retrieve the number of valid ezxmltext occurences
00144      *
00145      * @return void
00146      */
00147     public function xmlTextContentObjectAttributeCount()
00148     {
00149         if ( $this->xmlAttrCount === null )
00150         {
00151             $this->xmlAttrCount = eZContentObjectAttribute::fetchListByClassID( $this->xmlClassAttributeIds(), false, null, false, true );
00152         }
00153         return $this->xmlAttrCount;
00154     }
00155 
00156     /**
00157      * Add a message to the message buffer, to be displayed after processData has completed.
00158      *
00159      * @param string $message
00160      * @param string $label
00161      * @param bool $groupedEntry
00162      * @return void
00163      */
00164     public function outputString( $message, $label = null, $groupedEntry = false )
00165     {
00166         if ( $groupedEntry )
00167         {
00168             $this->finalOutputMessageArray[$this->outputEntryNumber]["messages"][] = $message;
00169         }
00170         else
00171         {
00172             $this->outputEntryNumber++;
00173             $this->finalOutputMessageArray[$this->outputEntryNumber] = array( "messages" => array( $message ),
00174                                                                               "label" => $label );
00175         }
00176     }
00177 
00178     /**
00179      * Search through valid ezxmltext occurrences, and fix missing url object links if
00180      * specified.
00181      *
00182      * @return void
00183      */
00184     public function processData()
00185     {
00186         while ( $this->processedCount < $this->xmlTextContentObjectAttributeCount() )
00187         {
00188             $limit = array( 'offset' => $this->offset,
00189                             'length' => $this->fetchLimit );
00190 
00191             $xmlAttributeChunk = eZContentObjectAttribute::fetchListByClassID( $this->xmlClassAttributeIds(), false, $limit, true, false );
00192 
00193             foreach ( $xmlAttributeChunk as $xmlAttr )
00194             {
00195                 $result = true;
00196                 // If the current entry has been logged, we don't increment the running number
00197                 // so that the entries can be displayed together on output.
00198                 $currentEntryLogged = false;
00199 
00200                 $currentId = $xmlAttr->attribute( 'id' );
00201                 $objectId = $xmlAttr->attribute( 'contentobject_id' );
00202                 $version = $xmlAttr->attribute( 'version' );
00203                 $languageCode = $xmlAttr->attribute( 'language_code' );
00204 
00205                 $label = "Attribute [id:{$currentId}] - [Object-id:{$objectId}] - [Version:{$version}] - [Language:{$languageCode}]";
00206 
00207                 $xmlText = eZXMLTextType::rawXMLText( $xmlAttr );
00208                 if ( empty( $xmlText ) )
00209                 {
00210                     if ( $this->verboseLevel > 0 )
00211                     {
00212                         $this->outputString( "Empty XML-data", $label, $currentEntryLogged );
00213                         $currentEntryLogged = true;
00214                     }
00215                     $result = false;
00216                     continue;
00217                 }
00218 
00219                 $dom = new DOMDocument( '1.0', 'utf-8' );
00220                 $success = $dom->loadXML( $xmlText );
00221                 if ( !$success )
00222                 {
00223                     if ( $this->verboseLevel > 0 )
00224                     {
00225                         $this->outputString( "XML not loaded correctly for attribute", $label, $currentEntryLogged );
00226                         $currentEntryLogged = true;
00227                     }
00228                     $result = false;
00229                     continue;
00230                 }
00231 
00232                 $linkNodes = $dom->getElementsByTagName( 'link' );
00233                 $urlIdArray = array();
00234 
00235                 foreach ( $linkNodes as $link )
00236                 {
00237                     // We are looking for external 'http://'-style links, not the internal
00238                     // object or node links.
00239                     if ( $link->hasAttribute( 'url_id' ) )
00240                     {
00241                         $urlIdArray[] = $link->getAttribute( 'url_id' );
00242                     }
00243                 }
00244 
00245                 if ( count( $urlIdArray ) > 0 )
00246                 {
00247                     if ( $this->verboseLevel > 0 )
00248                     {
00249                         $this->outputString( "Found http-link elements in xml-block", $label, $currentEntryLogged );
00250                         $currentEntryLogged = true;
00251                     }
00252 
00253                     $urlIdArray = array_unique( $urlIdArray );
00254 
00255                     foreach ( $urlIdArray as $url )
00256                     {
00257                         $linkObjectLink = eZURLObjectLink::fetch( $url, $currentId, $version );
00258                         if ( $linkObjectLink === null )
00259                         {
00260                             $result = false;
00261                             $this->outputString( "Missing url object link: [id:{$currentId}] - [version:{$version}] - [url:{$url}]", $label, $currentEntryLogged );
00262                             $currentEntryLogged = true;
00263                         }
00264 
00265                         $storedUrl = eZURL::url( $url );
00266                         if ( $storedUrl === false )
00267                         {
00268                             $result = false;
00269                             $this->outputString( "Missing URL, the referenced url does not exist, [url_id:{$url}]", $label, $currentEntryLogged );
00270                             $currentEntryLogged = true;
00271                         }
00272                     }
00273 
00274                     if ( $this->doFix and $linkObjectLink === null and $storedUrl !== false )
00275                     {
00276                         $this->outputString( "Reconstructing ezurl-object-link", $label, $currentEntryLogged );
00277                         $currentEntryLogged = true;
00278                         eZSimplifiedXMLInput::updateUrlObjectLinks( $xmlAttr, $urlIdArray );
00279                     }
00280                 }
00281 
00282                 $this->script->iterate( $this->cli, $result );
00283                 $label = null;
00284             }
00285 
00286             $this->processedCount += count( $xmlAttributeChunk );
00287             $this->offset += $this->fetchLimit;
00288             unset( $xmlAttributeChunk );
00289         }
00290     }
00291 
00292     /**
00293      * Print a summary of all the messages created during processData.
00294      *
00295      * @return void
00296      */
00297     public function showSummary()
00298     {
00299         foreach ( $this->finalOutputMessageArray as $messageEntry )
00300         {
00301             if ( $messageEntry['label'] !== null )
00302             {
00303                 $this->cli->output( $this->cli->stylize( 'bold', $messageEntry['label'] ) );
00304             }
00305 
00306             foreach ( $messageEntry['messages'] as $msg )
00307             {
00308                 $this->cli->output( $msg);
00309             }
00310             $this->cli->output();
00311         }
00312     }
00313 }
00314 ?>