Author: Alexandru Stanoi
Date: 2007-01-11 15:38:28 +0100 (Thu, 11 Jan 2007)
New Revision: 4491

Log:
- Added fetchParts() to ezcMail to return the mail parts of a mail (feature
  request #8303).
- Added walkParts() to ezcMail and the class ezcMailPartWalkContext which can
  be used to walk through all the parts in a mail and execute a callback
  function on each part (for example save mail parts to disk or a database).

Added:
   trunk/Mail/src/structs/walk_context.php
   trunk/Mail/tests/parser/walk_context_test.php
Modified:
   trunk/Mail/ChangeLog
   trunk/Mail/src/mail.php
   trunk/Mail/src/mail_autoload.php
   trunk/Mail/tests/parser/parser_test.php
   trunk/Mail/tests/suite.php

Modified: trunk/Mail/ChangeLog
===================================================================
--- trunk/Mail/ChangeLog        2007-01-11 14:01:30 UTC (rev 4490)
+++ trunk/Mail/ChangeLog        2007-01-11 14:38:28 UTC (rev 4491)
@@ -3,6 +3,11 @@
 
 - ezcMailTransportException inherits from ezcMailException, and not directly
   from ezcBaseException.
+- Added fetchParts() to ezcMail to return the mail parts of a mail (feature
+  request #8303).
+- Added walkParts() to ezcMail and the class ezcMailPartWalkContext which can
+  be used to walk through all the parts in a mail and execute a callback
+  function on each part (for example save mail parts to disk or a database).
 
 
 1.2.1 - [RELEASEDATE]

Modified: trunk/Mail/src/mail.php
===================================================================
--- trunk/Mail/src/mail.php     2007-01-11 14:01:30 UTC (rev 4490)
+++ trunk/Mail/src/mail.php     2007-01-11 14:38:28 UTC (rev 4491)
@@ -368,5 +368,142 @@
         }
         return parent::generateHeaders();
     }
+
+    /**
+     * Returns an array of mail parts from the current mail.
+     *
+     * The array returned contains objects of classes:
+     * - ezcMailText
+     * - ezcMailFile
+     * - ezcMailRfc822Digest
+     * If the method is called with $includeDigests as true, then the returned
+     * array will not contain ezcMailRfc822Digest objects, but instead the mail
+     * parts inside the digests.
+     * The parameter $filter can be used to restrict the returned mail parts,
+     * eg. $filter = array( 'ezcMailFile' ) to return only file mail parts.
+     *
+     * A typical use for this function is to get a list of attachments from a 
mail.
+     * Example:
+     * <code>
+     * // $mail is an ezcMail object
+     * $parts = $mail->fetchParts();
+     * // after the above line is executed, $parts will contain an array of 
mail parts objects,
+     * // for example one ezcMailText object ($parts[0]) and two 
ezcMailRfc822Digest objects ($parts[1] and $parts[2]).
+     * // the ezcMailText object will be used to render the mail text, and the
+     * // other two objects will be displayed as links ("view attachment")
+     *
+     * // when user clicks on one of the two attachments, the parts of that 
attachment
+     * // must be retrieved in order to render the attached digest:
+     * $subparts = $parts[1]->mail->fetchParts();
+     * // after the above line is executed, $subparts will contain an array of 
mail parts objects,
+     * // for example one ezcMailText object and one ezcMailFile object
+     * </code>
+     *
+     * @param array(string) $filter
+     * @param bool $includeDigests
+     * @return array(ezcMailPart)
+     */
+    public function fetchParts( $filter = null, $includeDigests = false )
+    {
+        $context = new ezcMailPartWalkContext( array( __CLASS__, 'collectPart' 
) );
+        $context->includeDigests = $includeDigests;
+        $context->filter = $filter;
+        $context->level = 0;
+        $this->walkParts( $context, $this );
+        return $context->getParts();
+    }
+
+    /**
+     * Walks recursively through the mail parts in the specified mail object.
+     *
+     * $context is an object of class ezcMailPartWalkContext, which must 
contain
+     * a valid callback function name to be applied to all mail parts. You can 
use
+     * the collectPart() method, or create your own callback function which can
+     * for example save the mail parts to disk or to a database.
+     *
+     * @see ezcMailPartWalkContext for the properties you can set to the walk 
context.
+     *
+     * Example:
+     * <code>
+     * class App
+     * {
+     *     public static function saveMailPart( $context, $mailPart )
+     *     {
+     *         // code to save the $mailPart object to disk
+     *     }
+     * }
+     *
+     * // use the saveMailPart() function as a callback in walkParts()
+     * // where $mail is an ezcMail object.
+     * $context = new ezcMailPartWalkContext( array( 'App', 'saveMailPart' ) );
+     * $context->includeDigests = true; // if you want to go through the 
digests in the mail
+     * $mail->walkParts( $context, $mail );
+     * </code>
+     *
+     * @param ezcMailPartWalkContext $context
+     * @param ezcMailPart $mail
+     */
+    public function walkParts( ezcMailPartWalkContext $context, ezcMailPart 
$mail )
+    {
+        $className = get_class( $mail );
+        $context->level++;
+        switch ( $className )
+        {
+            case 'ezcMail':
+            case 'ezcMailComposer':
+                $this->walkParts( $context, $mail->body );
+                break;
+
+            case 'ezcMailMultipartMixed':
+            case 'ezcMailMultipartAlternative':
+            case 'ezcMailMultipartDigest':
+                foreach ( $mail->getParts() as $part )
+                {
+                    $this->walkParts( $context, $part );
+                }
+                break;
+
+            case 'ezcMailMultipartRelated':
+                $this->walkParts( $context, $mail->getMainPart() );
+                foreach ( $mail->getRelatedParts() as $part )
+                {
+                    $this->walkParts( $context, $part );
+                }
+                break;
+
+            case 'ezcMailRfc822Digest':
+                if ( $context->includeDigests )
+                {
+                    $this->walkParts( $context, $mail->mail );
+                }
+                elseif ( empty( $context->filter ) || in_array( $className, 
$context->filter ) )
+                {
+                    call_user_func( $context->callbackFunction, $context, 
$mail );
+                }
+                break;
+
+            case 'ezcMailText':
+            case 'ezcMailFile':
+                if ( empty( $context->filter ) || in_array( $className, 
$context->filter ) )
+                {
+                    call_user_func( $context->callbackFunction, $context, 
$mail );
+                }
+                break;
+        }
+        $context->level--;
+    }
+
+    /**
+     * Saves $mail in the $context object.
+     *
+     * This function is used as a callback in the fetchParts() method.
+     *
+     * @param ezcMailPartWalkContext $context
+     * @param ezcMailPart $mail
+     */
+    protected static function collectPart( ezcMailPartWalkContext $context, 
ezcMailPart $mail )
+    {
+        $context->appendPart( $mail );
+    }
 }
 ?>

Modified: trunk/Mail/src/mail_autoload.php
===================================================================
--- trunk/Mail/src/mail_autoload.php    2007-01-11 14:01:30 UTC (rev 4490)
+++ trunk/Mail/src/mail_autoload.php    2007-01-11 14:38:28 UTC (rev 4491)
@@ -13,6 +13,7 @@
 return array(
     'ezcMailAddress'                => 'Mail/structs/mail_address.php',
     'ezcMailContentDispositionHeader'=> 
'Mail/structs/content_disposition_header.php',
+    'ezcMailPartWalkContext'        => 'Mail/structs/walk_context.php',
     'ezcMailPart'                   => 'Mail/interfaces/part.php',
     'ezcMailTransport'              => 'Mail/interfaces/transport.php',
     'ezcMail'                       => 'Mail/mail.php',

Added: trunk/Mail/src/structs/walk_context.php
===================================================================
--- trunk/Mail/src/structs/walk_context.php     2007-01-11 14:01:30 UTC (rev 
4490)
+++ trunk/Mail/src/structs/walk_context.php     2007-01-11 14:38:28 UTC (rev 
4491)
@@ -0,0 +1,188 @@
+<?php
+/**
+ * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @version //autogentag//
+ * @filesource
+ * @package Mail
+ */
+
+/**
+ * Use this class to create a context to be passed to the walkParts() method 
from ezcMail.
+ *
+ * Example:
+ * <code>
+ * class App
+ * {
+ *     public static function saveMailPart( $context, $mailPart )
+ *     {
+ *         // code to save the $mailPart object to disk
+ *     }
+ * }
+ *
+ * // use the saveMailPart() function as a callback in walkParts()
+ * // where $mail is an ezcMail object.
+ * $context = new ezcMailPartWalkContext( array( 'App', 'saveMailPart' ) );
+ * $context->includeDigests = true; // if you want to go through the digests 
in the mail
+ * $mail->walkParts( $context, $mail );
+ * </code>
+ *
+ * @property array(string) $filter
+ *           Used to restrict processing only to the specified mail part names.
+ *           If empty or null, then ezcMailText, ezcMailFile and 
ezcMailRfc822Digest
+ *           parts are processed. Usage e.g.: array( 'ezcMailFile' )
+ * @property callback $callbackFunction
+ *           Name of a function or array( 'class_name', 'function_name' )
+ * @property bool $includeDigests
+ *           If true then then ezcMailRfc822Digest parts are not processed by
+ *           the callback function, instead the mail parts inside the digests 
will
+ *           be available for processing.
+ * @property int $level
+ *           The current level in the mail part walk (0 = first level).
+ *
+ * @package Mail
+ * @version //autogentag//
+ */
+class ezcMailPartWalkContext
+{
+    /**
+     * An array of mail parts (retrieved recursively from a mail object).
+     *
+     * @var array(ezcMailPart)
+     */
+    protected $parts = array();
+
+    /**
+     * Holds the properties of this class.
+     *
+     * @var array(string=>mixed)
+     */
+    private $properties = array();
+
+    /**
+     * Constructs a new ezcMailPartWalkContext object.
+     *
+     * The parameter $callbackFunction must be a function name as string or as
+     * array( 'class_name', 'function_name' ).
+     *
+     * @param callback $callbackFunction
+     */
+    public function __construct( $callbackFunction )
+    {
+        $this->callbackFunction = $callbackFunction;
+        $this->level = 0;
+        $this->filter = array();
+        $this->includeDigests = false;
+    }
+
+    /**
+     * Sets the property $name to $value.
+     *
+     * @throws ezcBasePropertyNotFoundException
+     *         if the property $name does not exist
+     * @throws ezcBaseValueException
+     *         if $value is not appropiate for property $name
+     * @param string $name
+     * @param mixed $value
+     * @ignore
+     */
+    public function __set( $name, $value )
+    {
+        switch ( $name )
+        {
+            case 'level':
+                if ( !is_numeric( $value) || $value < 0 )
+                {
+                    throw new ezcBaseValueException( $name, $value, 'int >= 0' 
);
+                }
+                $this->properties[$name] = (int) $value;
+                break;
+
+            case 'includeDigests':
+                if ( !is_bool( $value ) )
+                {
+                    throw new ezcBaseValueException( $name, $value, 'bool' );
+                }
+                $this->properties[$name] = (bool) $value;
+                break;
+
+            case 'filter':
+                $this->properties[$name] = $value;
+                break;
+
+            case 'callbackFunction':
+                $this->properties[$name] = $value;
+                break;
+
+            default:
+                throw new ezcBasePropertyNotFoundException( $name );
+        }
+    }
+
+    /**
+     * Returns the value of the property $name.
+     *
+     * @throws ezcBasePropertyNotFoundException
+     *         if the property $name does not exist
+     * @param string $name
+     * @return mixed
+     * @ignore
+     */
+    public function __get( $name )
+    {
+        switch ( $name )
+        {
+            case 'level':
+            case 'filter':
+            case 'callbackFunction':
+            case 'includeDigests':
+                return $this->properties[$name];
+
+            default:
+                throw new ezcBasePropertyNotFoundException( $name );
+        }
+    }
+
+    /**
+     * Returns true if the property $name is set, otherwise false.
+     *
+     * @param string $name
+     * @return bool
+     * @ignore
+     */
+    public function __isset( $name )
+    {
+        switch ( $name )
+        {
+            case 'level':
+            case 'filter':
+            case 'callbackFunction':
+            case 'includeDigests':
+                return isset( $this->properties[$name] );
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Appends a part to the list of mail parts.
+     *
+     * @param ezcMailPart $part
+     */
+    public function appendPart( ezcMailPart $part )
+    {
+        $this->parts[] = $part;
+    }
+
+    /**
+     * Returns the mail parts.
+     *
+     * @return array(ezcMailPart)
+     */
+    public function getParts()
+    {
+        return $this->parts;
+    }
+}
+?>


Property changes on: trunk/Mail/src/structs/walk_context.php
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/Mail/tests/parser/parser_test.php
===================================================================
--- trunk/Mail/tests/parser/parser_test.php     2007-01-11 14:01:30 UTC (rev 
4490)
+++ trunk/Mail/tests/parser/parser_test.php     2007-01-11 14:38:28 UTC (rev 
4491)
@@ -920,5 +920,163 @@
         $mail = $mail[0];
         $this->assertEquals( '[EMAIL PROTECTED]', $mail->returnPath->email );
     }
+
+    public function testGetPartsNoFilterNoDigest()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'various/test-html-text-and-attachment' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( null, false );
+        $expected = array( 'ezcMailText',
+                           'ezcMailText',
+                           'ezcMailFile',
+                           'ezcMailFile'
+                           );
+        $this->assertEquals( 4, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsFilterNoDigest()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'various/test-html-text-and-attachment' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( array( 'ezcMailText' ), false );
+        $expected = array( 'ezcMailText',
+                           'ezcMailText'
+                           );
+        $this->assertEquals( 2, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsFilterNoDigestIncludeDigests()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'various/test-html-text-and-attachment' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( array( 'ezcMailText' ), true );
+        $expected = array( 'ezcMailText',
+                           'ezcMailText'
+                           );
+        $this->assertEquals( 2, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsNoFilter()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'pine/three_message_digest.mail' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( null, false );
+        $expected = array( 'ezcMailText',
+                           'ezcMailRfc822Digest',
+                           'ezcMailRfc822Digest',
+                           'ezcMailRfc822Digest',
+                           'ezcMailFile'
+                         );
+        $this->assertEquals( 5, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsNoFilterIncludeDigests()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'pine/three_message_digest.mail' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( null, true );
+        $expected = array( 'ezcMailText',
+                           'ezcMailText',
+                           'ezcMailText',
+                           'ezcMailFile',
+                           'ezcMailText',
+                           'ezcMailFile'
+                         );
+        $this->assertEquals( 6, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsFilter()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'pine/three_message_digest.mail' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( array( 'ezcMailFile' ), false );
+        $expected = array( 'ezcMailFile'
+                         );
+        $this->assertEquals( 1, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsFilterIncludeDigests()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'pine/three_message_digest.mail' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( array( 'ezcMailFile' ), true );
+        $expected = array( 'ezcMailFile',
+                           'ezcMailFile'
+                         );
+        $this->assertEquals( 2, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
+
+    public function testGetPartsFilterOnlyDigestIncludeDigests()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'pine/three_message_digest.mail' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( array( 'ezcMailRfc822Digest' ), true );
+        $this->assertEquals( 0, count( $parts ) );
+    }
+
+    public function testGetPartsDigestInDigestNoFilterIncludeDigests()
+    {
+        $parser = new ezcMailParser();
+        $set = new SingleFileSet( 'various/test-digest-in-digest' );
+        $mail = $parser->parseMail( $set );
+        $mail = $mail[0];
+        $parts = $mail->fetchParts( null, true );
+        $expected = array( 'ezcMailText',
+                           'ezcMailText',
+                           'ezcMailText',
+                           'ezcMailFile',
+                           'ezcMailFile',
+                           'ezcMailFile'
+                         );
+        $this->assertEquals( 6, count( $parts ) );
+        for ( $i = 0; $i < count( $parts ); $i++ )
+        {
+            $this->assertEquals( $expected[$i], get_class( $parts[$i] ) );
+        }
+    }
 }
 ?>

Added: trunk/Mail/tests/parser/walk_context_test.php
===================================================================
--- trunk/Mail/tests/parser/walk_context_test.php       2007-01-11 14:01:30 UTC 
(rev 4490)
+++ trunk/Mail/tests/parser/walk_context_test.php       2007-01-11 14:38:28 UTC 
(rev 4491)
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved.
+ * @license http://ez.no/licenses/new_bsd New BSD License
+ * @version //autogentag//
+ * @filesource
+ * @package Mail
+ * @subpackage Tests
+ */
+
+/**
+ * @package Mail
+ * @subpackage Tests
+ */
+class ezcMailPartWalkContextTest extends ezcTestCase
+{
+    public function testProperties()
+    {
+        $context = new ezcMailPartWalkContext( array( 'WalkContextTestApp', 
'saveMailPart' ) );
+        $context->includeDigests = true;
+        $context->level = 3;
+        $context->filter = array( 'ezcMailFile' );
+        $this->assertEquals( true, $context->includeDigests );
+        $this->assertEquals( 3, $context->level );
+        $this->assertEquals( array( 'ezcMailFile' ), $context->filter );
+    }
+
+    public function testPropertiesInvalid()
+    {
+        $context = new ezcMailPartWalkContext( array( 'WalkContextTestApp', 
'saveMailPart' ) );
+        try
+        {
+            $context->no_such_property = true;
+        }
+        catch ( ezcBasePropertyNotFoundException $e )
+        {
+        }
+
+        try
+        {
+            $context->level = -1;
+        }
+        catch ( ezcBaseValueException $e )
+        {
+        }
+
+        try
+        {
+            $context->includeDigests = "yes";
+        }
+        catch ( ezcBaseValueException $e )
+        {
+        }
+
+        try
+        {
+            $test = $context->no_such_property;
+        }
+        catch ( ezcBasePropertyNotFoundException $e )
+        {
+        }
+    }
+
+    public function testIsSet()
+    {
+        $context = new ezcMailPartWalkContext( array( 'WalkContextTestApp', 
'saveMailPart' ) );
+        $this->assertEquals( true, isset( $context->includeDigests ) );
+        $this->assertEquals( true, isset( $context->filter ) );
+        $this->assertEquals( true, isset( $context->level ) );
+        $this->assertEquals( true, isset( $context->callbackFunction ) );
+        $this->assertEquals( false, isset( $context->no_such_property ) );
+    }
+
+    public static function suite()
+    {
+         return new PHPUnit_Framework_TestSuite( "ezcMailPartWalkContextTest" 
);
+    }
+}
+
+/**
+ * Test class.
+ */
+class WalkContextTestApp
+{
+    public static function saveMailPart()
+    {
+    }
+}
+?>


Property changes on: trunk/Mail/tests/parser/walk_context_test.php
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/Mail/tests/suite.php
===================================================================
--- trunk/Mail/tests/suite.php  2007-01-11 14:01:30 UTC (rev 4490)
+++ trunk/Mail/tests/suite.php  2007-01-11 14:38:28 UTC (rev 4491)
@@ -31,6 +31,7 @@
 require_once( "tutorial_examples.php" );
 require_once( "parser/parser_test.php" );
 require_once( "parser/headers_holder_test.php" );
+require_once( "parser/walk_context_test.php" );
 require_once( "parser/parts/multipart_mixed_test.php" );
 require_once( "parser/rfc2231_implementation_test.php" );
 require_once( "header_folder_test.php" );
@@ -65,6 +66,7 @@
         $this->addTest( ezcMailTransportVariableTest::suite() );
         $this->addTest( ezcMailTutorialExamples::suite() );
         $this->addTest( ezcMailParserTest::suite() );
+        $this->addTest( ezcMailPartWalkContextTest::suite() );
         $this->addTest( ezcMailHeadersHolderTest::suite() );
         $this->addTest( ezcMailMultipartMixedParserTest::suite() );
         $this->addTest( ezcMailRfc2231ImplementationTest::suite() );

-- 
svn-components mailing list
svn-components@lists.ez.no
http://lists.ez.no/mailman/listinfo/svn-components

Reply via email to