Skip to content

Commit

Permalink
[mms] Add recurisve iteration to the Horde_Mime_Part object.
Browse files Browse the repository at this point in the history
  • Loading branch information
slusarz committed Feb 26, 2015
1 parent df87294 commit 1db1a68
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 67 deletions.
11 changes: 11 additions & 0 deletions framework/Mime/doc/Horde/Mime/UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,23 @@ Upgrading to 2.8

These static properties are deprecated.

- Iteration

The object can now be iterated through to access the subparts.
partIterator() can be used to obtain a RecursiveIteratorIterator that
will iterator through all descendants.

- clearContentTypeParameter()

This method is deprecated. Use Horde_Mime_Part#setContentTypeParameter()
instead with null as the second argument to delete a Content-Type
parameter.

- contentTypeMap()

This method is deprecated. Use partIterator() to recursively iterate
through the parts instead.

- setContentTypeParameter()

Passing null as the second argument will cause the Content-Type
Expand Down
182 changes: 121 additions & 61 deletions framework/Mime/lib/Horde/Mime/Part.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Mime
*/
class Horde_Mime_Part implements ArrayAccess, Countable, Serializable
class Horde_Mime_Part
implements ArrayAccess, Countable, RecursiveIterator, Serializable
{
/* Serialized version. */
const VERSION = 2;
Expand Down Expand Up @@ -1130,8 +1131,7 @@ public function toString($options = array())

$boundary = trim($this->getContentTypeParameter('boundary'), '"');

reset($this->_parts);
while (list(,$part) = each($this->_parts)) {
foreach ($this as $part) {
$parts[] = $eol . '--' . $boundary . $eol;
$tmp = $part->toString($options);
if ($part->getEOL() != $eol) {
Expand Down Expand Up @@ -1330,8 +1330,7 @@ public function getBytes($approx = false)
}

$bytes = 0;
reset($this->_parts);
while (list(,$part) = each($this->_parts)) {
foreach ($this as $part) {
$bytes += $part->getBytes($approx);
}
return $bytes;
Expand Down Expand Up @@ -1478,8 +1477,8 @@ public function buildMimeIds($id = null, $rfc822 = false)
$this->setMimeId($id . '0');
}
$i = 1;
foreach (array_keys($this->_parts) as $val) {
$this->_parts[$val]->buildMimeIds($id . ($i++));
foreach ($this as $val) {
$val->buildMimeIds($id . ($i++));
}
}
} else {
Expand All @@ -1488,59 +1487,22 @@ public function buildMimeIds($id = null, $rfc822 = false)
? ((substr($id, -2) === '.0') ? substr($id, 0, -1) : ($id . '.'))
: '';

if ($this->getType() == 'message/rfc822') {
if (count($this->_parts)) {
reset($this->_parts);
$this->_parts[key($this->_parts)]->buildMimeIds($id, true);
}
} elseif (!empty($this->_parts)) {
$i = 1;
foreach (array_keys($this->_parts) as $val) {
$this->_parts[$val]->buildMimeIds($id . ($i++));
if (count($this)) {
if ($this->getType() == 'message/rfc822') {
$this->rewind();
$this->current()->buildMimeIds($id, true);
} else {
$i = 1;
foreach ($this as $val) {
$val->buildMimeIds($id . ($i++));
}
}
}
}

$this->_status &= ~self::STATUS_REINDEX;
}

/**
* Returns a mapping of all MIME IDs to their content-types.
*
* @param boolean $sort Sort by MIME ID?
*
* @return array Keys: MIME ID; values: content type.
*/
public function contentTypeMap($sort = true)
{
$map = array($this->getMimeId() => $this->getType());
foreach ($this->_parts as $val) {
$map += $val->contentTypeMap(false);
}

if ($sort) {
uksort($map, array($this, '_contentTypeMapSort'));
}

return $map;
}

/**
*/
protected function _contentTypeMapSort($a, $b)
{
if (substr($a, -2) === '.0') {
if (substr($a, 0, -2) == $b) {
return -1;
}
} elseif ((substr($b, 0, -2) === '.0') &&
(substr($b, 0, -2) == $a)) {
return 1;
}

return strnatcmp($a, $b);
}

/**
* Is this the base MIME part?
*
Expand Down Expand Up @@ -1692,22 +1654,42 @@ public function send($email, $headers, Horde_Mail_Transport $mailer,
*/
public function findBody($subtype = null)
{
$initial_id = $this->getMimeId();
$this->buildMimeIds();

foreach ($this->contentTypeMap() as $mime_id => $mime_type) {
if ((strpos($mime_type, 'text/') === 0) &&
(!$initial_id || (intval($mime_id) == 1)) &&
(is_null($subtype) || (substr($mime_type, 5) == $subtype)) &&
($part = $this->getPart($mime_id)) &&
($part->getDisposition() != 'attachment')) {
return $mime_id;
foreach ($this->partIterator() as $val) {
$id = $val->getMimeId();

if (($val->getPrimaryType() == 'text') &&
((intval($id) === 1) || !$this->getMimeId()) &&
(is_null($subtype) || ($val->getSubType() == $subtype)) &&
($val->getDisposition() !== 'attachment')) {
return $id;
}
}

return null;
}

/**
* Returns the recursive iterator needed to iterate through this part.
* The first entry will be this part itself.
*
* @since 2.8.0
*
* @return Iterator Recursive Iterator.
*/
public function partIterator()
{
$i = new AppendIterator();
$i->append(new ArrayIterator(array($this)));
$i->append(new RecursiveIteratorIterator(
$this,
RecursiveIteratorIterator::SELF_FIRST
));

return $i;
}

/**
* Write data to a stream.
*
Expand Down Expand Up @@ -2225,6 +2207,70 @@ public function count()
return count($this->_parts);
}

/* RecursiveIterator methods. */

/**
* @since 2.8.0
*/
public function current()
{
return (($key = $this->key()) !== null)
? $this->_parts[$key]
: null;
}

/**
* @since 2.8.0
*/
public function key()
{
return (isset($this->_temp['iterate']) && isset($this->_parts[$this->_temp['iterate']]))
? $this->_temp['iterate']
: null;
}

/**
* @since 2.8.0
*/
public function next()
{
++$this->_temp['iterate'];
}

/**
* @since 2.8.0
*/
public function rewind()
{
$this->_parts = array_values($this->_parts);
reset($this->_parts);
$this->_temp['iterate'] = key($this->_parts);
}

/**
* @since 2.8.0
*/
public function valid()
{
return ($this->key() !== null);
}

/**
* @since 2.8.0
*/
public function hasChildren()
{
return (($curr = $this->current()) && count($curr));
}

/**
* @since 2.8.0
*/
public function getChildren()
{
return $this->current();
}

/* Serializable methods. */

/**
Expand Down Expand Up @@ -2332,4 +2378,18 @@ public function clearContentTypeParameter($label)
$this->setContentTypeParam($label, null);
}

/**
* @deprecated Use iterator instead.
*/
public function contentTypeMap($sort = true)
{
$map = array();

foreach ($this->partIterator() as $val) {
$map[$val->getMimeId()] = $val->getType();
}

return $map;
}

}
15 changes: 9 additions & 6 deletions framework/Mime/lib/Horde/Mime/Related.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,18 @@ public function __construct(Horde_Mime_Part $mime_part)
throw new InvalidArgumentException('MIME part must be of type multipart/related');
}

$ids = array_keys($mime_part->contentTypeMap());
$related_id = $mime_part->getMimeId();
$id = null;
$ids = array();
$related_id = $mime_part->getMimeId();

/* Build a list of parts -> CIDs. */
foreach ($ids as $val) {
if ((strcmp($related_id, $val) !== 0) &&
($cid = $mime_part->getPart($val)->getContentId())) {
$this->_cids[$val] = $cid;
foreach ($mime_part->partIterator() as $val) {
$part_id = $val->getMimeId();
$ids[] = $part_id;

if ((strcmp($related_id, $part_id) !== 0) &&
($cid = $val->getContentId())) {
$this->_cids[$part_id] = $cid;
}
}

Expand Down
2 changes: 2 additions & 0 deletions framework/Mime/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
</stability>
<license uri="http://www.horde.org/licenses/lgpl21">LGPL-2.1</license>
<notes>
* [mms] Add recurisve iteration to the Horde_Mime_Part object.
* [mms] Work around broken line-oriented data when transfer encoding (Bug #13835).
* [mms] Fix regression in using the deprecated Horde_Mime::generateMessageId() method (Bug #13846).
* [mms] Add Horde_Mime_Headers_ContentId class.
Expand Down Expand Up @@ -1667,6 +1668,7 @@
<date>2015-02-16</date>
<license uri="http://www.horde.org/licenses/lgpl21">LGPL-2.1</license>
<notes>
* [mms] Add recurisve iteration to the Horde_Mime_Part object.
* [mms] Work around broken line-oriented data when transfer encoding (Bug #13835).
* [mms] Fix regression in using the deprecated Horde_Mime::generateMessageId() method (Bug #13846).
* [mms] Add Horde_Mime_Headers_ContentId class.
Expand Down
42 changes: 42 additions & 0 deletions framework/Mime/test/Horde/Mime/PartTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,34 @@ public function testSerializeUpgradeFromVersion1()
);
}

public function testIteration()
{
$iterator = $this->_getTestPart()->partIterator();

$ids = array(
'0',
'1',
'2',
'3',
'3.1',
'3.2',
'3.2.1',
'3.2.2'
);
reset($ids);

foreach ($iterator as $val) {
$this->assertEquals(
current($ids),
$val->getMimeId()
);

next($ids);
}

$this->assertFalse(current($ids));
}

protected function _getTestPart()
{
$part = new Horde_Mime_Part();
Expand All @@ -949,6 +977,20 @@ protected function _getTestPart()
$part3_1->setContents('Test 2');
$part3->addPart($part3_1);

$part3_2 = new Horde_Mime_Part();
$part3_2->setType('multipart/mixed');
$part3->addPart($part3_2);

$part3_2_1 = new Horde_Mime_Part();
$part3_2_1->setType('text/plain');
$part3_2_1->setContents('Test 3.2.1');
$part3_2->addPart($part3_2_1);

$part3_2_2 = new Horde_Mime_Part();
$part3_2_2->setType('application/octet-stream');
$part3_2_2->setContents('Test 3.2.2');
$part3_2->addPart($part3_2_2);

$part->buildMimeIds();

return $part;
Expand Down

0 comments on commit 1db1a68

Please sign in to comment.