Skip to content

Commit 7db2730

Browse files
committed
#2 Writer MSProjectExchange
1 parent e429db8 commit 7db2730

File tree

4 files changed

+372
-5
lines changed

4 files changed

+372
-5
lines changed

docs/intro.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ Writers
4343
+---------------------------+----------------------+--------+-------+
4444
| **Document Informations** | | | |
4545
+---------------------------+----------------------+--------+-------+
46-
| **Project** | Task | ||
46+
| **Project** | Task | ||
4747
+---------------------------+----------------------+--------+-------+
48-
| | Resource | ||
48+
| | Resource | ||
4949
+---------------------------+----------------------+--------+-------+
50-
| | Allocation | ||
50+
| | Allocation | ||
5151
+---------------------------+----------------------+--------+-------+
5252

5353
Readers

docs/references.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
References
44
==========
55

6-
GanttProject
6+
GanttProject (GAN)
77
---------------------
88

99
- `Website <http://ganttproject.biz>`__
1010

11-
MSProjectExchange
11+
MSProjectExchange (MPX)
1212
---------------------
1313

1414
- `MSDN : Description of the MPX Project File Exchange Format <http://support.microsoft.com/kb/270139>`__
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
<?php
2+
/**
3+
* This file is part of PHPProject - A pure PHP library for reading and writing
4+
* presentations documents.
5+
*
6+
* PHPProject is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* file that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12+
*
13+
* @link https://github.com/PHPOffice/PHPProject
14+
* @copyright 2009-2014 PHPProject contributors
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
namespace PhpOffice\PhpProject\Writer;
19+
20+
use PhpOffice\PhpProject\PhpProject;
21+
use PhpOffice\PhpProject\Resource;
22+
use PhpOffice\PhpProject\Task;
23+
use PhpOffice\PhpProject\Shared\XMLWriter;
24+
25+
/**
26+
* MsProjectMPx
27+
*
28+
* @category PHPProject
29+
* @package PHPProject
30+
* @copyright Copyright (c) 2012 - 2012 PHPProject (https://github.com/PHPOffice/PHPProject)
31+
*/
32+
class MsProjectMPX
33+
{
34+
/**
35+
* PHPProject object
36+
*
37+
* @var \PhpOffice\PhpProject\PhpProject
38+
*/
39+
private $phpProject;
40+
41+
/**
42+
* Content to write in File
43+
* @var string[]
44+
*/
45+
private $fileContent = array();
46+
47+
48+
/**
49+
* Create a new PHPProject_Writer_GanttProject
50+
*
51+
* @param PHPProject $phpProject
52+
*/
53+
public function __construct (PhpProject $phpProject)
54+
{
55+
$this->phpProject = $phpProject;
56+
}
57+
58+
/**
59+
*
60+
* @param string $pFilename
61+
* @throws Exception
62+
*/
63+
public function save ($pFilename)
64+
{
65+
$arrProjectInfo = $this->sanitizeProject();
66+
67+
$this->writeRecordMPX();
68+
// Project Header
69+
$this->writeRecord30($arrProjectInfo);
70+
// Text Resource Table Definition
71+
$this->writeRecord40();
72+
// Numeric Resource Table Definition
73+
$this->writeRecord41();
74+
// Resources
75+
foreach ($this->phpProject->getAllResources() as $oResource) {
76+
$this->writeRecord50($oResource);
77+
}
78+
// Text Task Table Definition
79+
$this->writeRecord60();
80+
// Numeric Task Table Definition
81+
$this->writeRecord61();
82+
// Tasks
83+
foreach ($this->phpProject->getAllTasks() as $oTask) {
84+
$this->writeRecord70($oTask);
85+
}
86+
87+
// Writing XML Object in file
88+
// Open file
89+
if (file_exists($pFilename) && !is_writable($pFilename)) {
90+
throw new \Exception("Could not open file $pFilename for writing.");
91+
}
92+
$fileHandle = fopen($pFilename, 'wb+');
93+
// Write Content
94+
fwrite($fileHandle, implode(PHP_EOL, $this->fileContent));
95+
// Close file
96+
fclose($fileHandle);
97+
}
98+
99+
/**
100+
* @return multitype:Ambigous <number, unknown>
101+
*/
102+
private function sanitizeProject ()
103+
{
104+
// Info Project
105+
$minDate = 0;
106+
// Browse all tasks
107+
$arrTasks = $this->phpProject->getAllTasks();
108+
foreach ($arrTasks as $oTask) {
109+
if ($oTask->getTaskCount() == 0) {
110+
$this->sanitizeTask($oTask);
111+
} else {
112+
$this->sanitizeTaskParent($oTask);
113+
}
114+
$tStartDate = $oTask->getStartDate();
115+
if ($minDate == 0 || $tStartDate < $minDate) {
116+
$minDate = $tStartDate;
117+
}
118+
}
119+
// Browse all Resources
120+
$numResource = 0;
121+
foreach ($this->phpProject->getAllResources() as $oResource) {
122+
$oResource->setIndex($numResource);
123+
$numResource++;
124+
}
125+
126+
return array(
127+
'date_start' => (int)$minDate
128+
);
129+
}
130+
131+
/**
132+
* Permits to clean a task
133+
* - If the duration is not filled, but the end date is, we calculate it.
134+
* - If the end date is not filled, but the duration is, we calculate it.
135+
* @param PHPProject_Task $oTask
136+
*/
137+
private function sanitizeTask (Task $oTask)
138+
{
139+
$pDuration = $oTask->getDuration();
140+
$pEndDate = $oTask->getEndDate();
141+
$pStartDate = $oTask->getStartDate();
142+
143+
if (is_null($pDuration) && !is_null($pEndDate)) {
144+
$iTimeDiff = $pEndDate - $pStartDate;
145+
$iNumDays = $iTimeDiff / 60 / 60 / 24;
146+
$oTask->setDuration($iNumDays + 1);
147+
} elseif (!is_null($pDuration) && is_null($pEndDate)) {
148+
$oTask->setEndDate($pStartDate + ($pDuration * 24 * 60 * 60));
149+
}
150+
}
151+
152+
/**
153+
* Permits to clean parent task and calculate parent data like total duration,
154+
* date start and complete average.
155+
* @param PHPProject_Task $oParentTask
156+
*/
157+
private function sanitizeTaskParent (Task $oParentTask)
158+
{
159+
$arrTasksChilds = $oParentTask->getTasks();
160+
161+
$iProgress = 0;
162+
$tStartDate = null;
163+
$tEndDate = null;
164+
foreach ($arrTasksChilds as $oTaskChild) {
165+
if ($oTaskChild->getTaskCount() == 0) {
166+
$this->sanitizeTask($oTaskChild);
167+
} else {
168+
$this->sanitizeTaskParent($oTaskChild);
169+
}
170+
171+
$iProgress += $oTaskChild->getProgress();
172+
if (is_null($tStartDate)) {
173+
$tStartDate = $oTaskChild->getStartDate();
174+
} elseif ($tStartDate > $oTaskChild->getStartDate()) {
175+
$tStartDate = $oTaskChild->getStartDate();
176+
}
177+
178+
if (is_null($tEndDate)) {
179+
$tEndDate = $oTaskChild->getEndDate();
180+
} elseif ($tEndDate < $oTaskChild->getEndDate()) {
181+
$tEndDate = $oTaskChild->getEndDate();
182+
}
183+
}
184+
$oParentTask->setProgress($iProgress / $oParentTask->getTaskCount());
185+
$oParentTask->setStartDate($tStartDate);
186+
$oParentTask->setEndDate($tEndDate);
187+
$oParentTask->setDuration((($tEndDate - $tStartDate) / 60 / 60 / 24) + 1);
188+
}
189+
190+
/**
191+
* Record MPX
192+
*/
193+
private function writeRecordMPX()
194+
{
195+
$this->fileContent[] = 'MPX;Microsoft Project for Windows;4.0;ANSI';
196+
}
197+
198+
/**
199+
* Record "Project Header"
200+
*/
201+
private function writeRecord30(array $arrProjectInfo)
202+
{
203+
$this->fileContent[] = '30;Project1;;;Standard;'.date('d/m/Y', $arrProjectInfo['date_start']).';;0;'.date('d/m/Y').';;$0,00;$0,00;$0,00;0h;0h;0h;0%;0d;0d;0d;0%;;;;;0d;0d';
204+
}
205+
206+
/**
207+
* Record "Text Resource Table Definition"
208+
*/
209+
private function writeRecord40()
210+
{
211+
$this->fileContent[] = '40;ID;Name';
212+
}
213+
214+
/**
215+
* Record "Numeric Resource Table Definition"
216+
*/
217+
private function writeRecord41()
218+
{
219+
$this->fileContent[] = '41;40;1';
220+
}
221+
222+
/**
223+
* Record "Resource"
224+
* @param Resource $oResource
225+
*/
226+
private function writeRecord50(Resource $oResource)
227+
{
228+
$this->fileContent[] = '50;'.$oResource->getIndex().';'.$oResource->getTitle();
229+
}
230+
231+
/**
232+
* Record "Text Task Table Definition"
233+
*/
234+
private function writeRecord60()
235+
{
236+
$this->fileContent[] = '60;ID;Name;Duration;% Complete;Start';
237+
}
238+
239+
/**
240+
* Record "Numeric Task Table Definition"
241+
*/
242+
private function writeRecord61()
243+
{
244+
$this->fileContent[] = '61;90;1;40;44;50';
245+
}
246+
247+
/**
248+
* Record "Task"
249+
* @param Task $oTask
250+
*/
251+
private function writeRecord70(Task $oTask)
252+
{
253+
$this->fileContent[] = '70;'.$oTask->getIndex().';'.$oTask->getName().';'.$oTask->getDuration().'d;'.number_format($oTask->getProgress(), 1).';'.date('d/m/Y', $oTask->getStartDate());
254+
255+
foreach ($oTask->getResources() as $oResource) {
256+
$this->writeRecord75($oResource);
257+
}
258+
259+
foreach ($oTask->getTasks() as $oSubTask) {
260+
$this->writeRecord70($oSubTask);
261+
}
262+
}
263+
264+
/**
265+
* Record "Resource Assignment"
266+
* @param Resource $oResource
267+
*/
268+
private function writeRecord75(Resource $oResource)
269+
{
270+
$this->fileContent[] = '75;'.$oResource->getIndex().';1;;;;;;;;;;;';
271+
}
272+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
/**
3+
* This XMLWriter is part of PHPPowerPoint - A pure PHP library for reading and writing
4+
* presentations documents.
5+
*
6+
* PHPPowerPoint is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* XMLWriter that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPPowerPoint/contributors.
12+
*
13+
* @copyright 2009-2014 PHPPowerPoint contributors
14+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
15+
* @link https://github.com/PHPOffice/PHPPowerPoint
16+
*/
17+
18+
namespace PhpOffice\PhpProject\Tests\Writer;
19+
20+
use PhpOffice\PhpProject\IOFactory;
21+
use PhpOffice\PhpProject\PhpProject;
22+
use PhpOffice\PhpProject\Writer\MsProjectMPX;
23+
24+
/**
25+
* Test class for MsProjectMPX
26+
*
27+
* @coversDefaultClass PhpOffice\PhpProject\Writer\MsProjectMPX
28+
*/
29+
class MsProjectMPXTest extends \PHPUnit_Framework_TestCase
30+
{
31+
public function testSave()
32+
{
33+
$fileOutput = tempnam(sys_get_temp_dir(), 'PHPPROJECT');
34+
$oPHPProject = new PhpProject();
35+
36+
$oResource = $oPHPProject->createResource();
37+
$oResource->setTitle('ResourceTest');
38+
39+
$oTask1 = $oPHPProject->createTask();
40+
$oTask1->setName('Task1Test');
41+
$oTask1->addResource($oResource);
42+
43+
$oTask1Child = $oTask1->createTask();
44+
$oTask1Child->setName('TaskChildTest');
45+
46+
$oTask1ChildChild1 = $oTask1Child->createTask();
47+
$oTask1ChildChild1->setName('TaskChildChild1Test');
48+
$oTask1ChildChild1->setStartDate('2014-08-07');
49+
$oTask1ChildChild1->setDuration(2);
50+
$oTask1ChildChild2 = $oTask1Child->createTask();
51+
$oTask1ChildChild2->setName('TaskChildChild2Test');
52+
$oTask1ChildChild2->setStartDate('2014-08-06');
53+
$oTask1ChildChild2->setEndDate('2014-08-10');
54+
55+
$oTask2 = $oPHPProject->createTask();
56+
$oTask2->setStartDate('2014-08-07');
57+
$oTask2->setEndDate('2014-08-13');
58+
$oTask2->setName('Task2Test');
59+
60+
$xmlWriter = IOFactory::createWriter($oPHPProject, 'MsProjectMPX');
61+
$xmlWriter->save($fileOutput);
62+
63+
$content = file_get_contents($fileOutput);
64+
$content = explode(PHP_EOL, $content);
65+
66+
foreach ($content as $line) {
67+
$line = explode(';', $line);
68+
69+
switch ($line[0]) {
70+
case 'MPX':
71+
$this->assertEquals('Microsoft Project for Windows', $line[1]);
72+
break;
73+
}
74+
}
75+
}
76+
77+
/**
78+
* @expectedException \Exception
79+
* @expectedExceptionMessage Could not open file
80+
*/
81+
public function testSaveException()
82+
{
83+
$fileOutput = tempnam(sys_get_temp_dir(), 'PHPPROJECT');
84+
file_put_contents($fileOutput, 'AA');
85+
chmod($fileOutput, 0044);
86+
87+
$oPHPProject = new PhpProject();
88+
89+
$oTask1 = $oPHPProject->createTask();
90+
$oTask1->setName('Task1Test');
91+
92+
$xmlWriter = IOFactory::createWriter($oPHPProject, 'MsProjectMPX');
93+
$xmlWriter->save($fileOutput);
94+
}
95+
}

0 commit comments

Comments
 (0)