Add "add file" button to display page
[phorkie.git] / src / phorkie / Repository / Post.php
1 <?php
2 namespace phorkie;
3
4 class Repository_Post
5 {
6     public $repo;
7
8     /**
9      * When a new file is created during processing, its name
10      * is stored here for later use.
11      *
12      * @var string
13      */
14     public $newfileName;
15
16
17
18     public function __construct(Repository $repo = null)
19     {
20         $this->repo = $repo;
21     }
22
23     /**
24      * Processes the POST data, changes description and files
25      *
26      * @return boolean True if the post was successful
27      */
28     public function process($postData, $sessionData)
29     {
30         if (!isset($postData['files'])) {
31             return false;
32         }
33         if (!$this->hasContent($postData)) {
34             return false;
35         }
36
37         if (!$this->repo) {
38             $this->repo = $this->createRepo();
39         }
40
41         $vc = $this->repo->getVc();
42
43
44         $bChanged = false;
45         $bCommit  = false;
46         if ($postData['description'] != $this->repo->getDescription()) {
47             $this->repo->setDescription($postData['description']);
48             $bChanged = true;
49         }
50
51         foreach ($postData['files'] as $num => $arFile) {
52             $bUpload = false;
53             if ($_FILES['files']['error'][$num]['upload'] == 0) {
54                 //valid file upload
55                 $bUpload = true;
56             } else if ($arFile['content'] == '' && $arFile['name'] == '') {
57                 //empty (new) file
58                 continue;
59             }
60
61             $orignalName = Tools::sanitizeFilename($arFile['original_name']);
62             $name        = Tools::sanitizeFilename($arFile['name']);
63
64             if ($arFile['type'] == '_auto_') {
65                 //FIXME: upload
66                 $arFile['type'] = $this->getType($arFile['content']);
67             }
68
69             if ($name == '') {
70                 if ($bUpload) {
71                     $name = Tools::sanitizeFilename(
72                         $_FILES['files']['name'][$num]['upload']
73                     );
74                 } else {
75                     $name = $this->getNextNumberedFile('phork')
76                         . '.' . $arFile['type'];
77                 }
78             }
79
80             $bNew = false;
81             $bDelete = false;
82             if (!isset($orignalName) || $orignalName == '') {
83                 //new file
84                 $bNew = true;
85                 if (strpos($name, '.') === false) {
86                     //automatically append file extension if none is there
87                     $name .= '.' . $arFile['type'];
88                 }
89                 $this->newfileName = $name;
90             } else if (!$this->repo->hasFile($orignalName)) {
91                 //unknown file
92                 //FIXME: Show error message
93                 continue;
94             } else if (isset($arFile['delete']) && $arFile['delete'] == 1) {
95                 $bDelete = true;
96             } else if ($orignalName != $name) {
97                 if (strpos($name, '/') === false) {
98                     //ignore names with a slash in it, would be new directory
99                     //FIXME: what to do with overwrites?
100                     $vc->getCommand('mv')
101                         ->addArgument($orignalName)
102                         ->addArgument($name)
103                         ->execute();
104                     $bCommit = true;
105                 } else {
106                     $name = $orignalName;
107                 }
108             }
109
110             $file = $this->repo->getFileByName($name, false);
111             if ($bDelete) {
112                 $command = $vc->getCommand('rm')
113                     ->addArgument($file->getFilename())
114                     ->execute();
115                 $bCommit = true;
116             } else if ($bUpload) {
117                 move_uploaded_file(
118                     $_FILES['files']['tmp_name'][$num]['upload'],
119                     $file->getFullPath()
120                 );
121                 $command = $vc->getCommand('add')
122                     ->addArgument($file->getFilename())
123                     ->execute();
124                 $bCommit = true;
125             } else if ($bNew
126                 || (isset($arFile['content'])
127                     && $file->getContent() != $arFile['content']
128                 )
129             ) {
130                 $dir = dirname($file->getFullPath());
131                 if (!is_dir($dir)) {
132                     mkdir($dir, 0777, true);
133                 }
134                 file_put_contents($file->getFullPath(), $arFile['content']);
135                 $command = $vc->getCommand('add')
136                     ->addArgument($file->getFilename())
137                     ->execute();
138                 $bCommit = true;
139             }
140         }
141
142         if (isset($sessionData['identity'])) {
143             $notes = $sessionData['identity'];
144         } else {
145             $notes = $sessionData['ipaddr'];
146         }
147
148         if ($bCommit) {
149             $vc->getCommand('commit')
150                 ->setOption('message', '')
151                 ->setOption('allow-empty-message')
152                 ->setOption('no-edit')
153                 ->setOption(
154                     'author',
155                     $sessionData['name'] . ' <' . $sessionData['email'] . '>'
156                 )
157                 ->execute();
158             //FIXME: git needs ref BEFORE add
159             //quick hack until http://pear.php.net/bugs/bug.php?id=19605 is fixed
160             //also waiting for https://pear.php.net/bugs/bug.php?id=19623
161             $vc->getCommand('notes --ref=identity add')
162                 ->setOption('force')
163                 ->setOption('message', "$notes")
164                 ->execute();
165             //update info for dumb git HTTP transport
166             //the post-update hook should do that IMO, but does not somehow
167             $vc->getCommand('update-server-info')->execute();
168
169             $bChanged = true;
170         }
171
172         if ($bChanged) {
173             //FIXME: index changed files only
174             //also handle file deletions
175             $db = new Database();
176             $not = new Notificator();
177             if ($bNew) {
178                 $db->getIndexer()->addRepo($this->repo);
179                 $not->create($this->repo);
180             } else {
181                 $commits = $this->repo->getHistory();
182                 $db->getIndexer()->updateRepo(
183                     $this->repo,
184                     $commits[count($commits)-1]->committerTime,
185                     $commits[0]->committerTime
186                 );
187                 $not->edit($this->repo);
188             }
189         }
190
191         return true;
192     }
193
194     protected function hasContent($postData)
195     {
196         foreach ($postData['files'] as $num => $arFile) {
197             if ($_FILES['files']['error'][$num]['upload'] == 0) {
198                 return true;
199             }
200             if (isset($arFile['content']) && $arFile['content'] != '') {
201                 return true;
202             }
203             if (isset($arFile['name']) && $arFile['name'] != '') {
204                 //binary files do not have content
205                 return true;
206             }
207             if (isset($arFile['delete']) && $arFile['delete'] != '') {
208                 //binary files do not have content
209                 return true;
210             }
211         }
212         return false;
213     }
214
215     public function createRepo()
216     {
217         $rs = new Repositories();
218         $repo = $rs->createNew();
219         $vc = $repo->getVc();
220         $vc->getCommand('init')
221             //this should be setOption, but it fails with a = between name and value
222             ->addArgument('--separate-git-dir')
223             ->addArgument(
224                 $GLOBALS['phorkie']['cfg']['gitdir'] . '/' . $repo->id . '.git'
225             )
226             ->addArgument($repo->workDir)
227             ->execute();
228
229         $rs = new Repository_Setup($repo);
230         $rs->afterInit();
231
232         return $repo;
233     }
234
235     public function getNextNumberedFile($prefix)
236     {
237         $num = -1;
238         do {
239             ++$num;
240             $files = glob($this->repo->workDir . '/' . $prefix . $num . '.*');
241         } while (count($files));
242
243         return $prefix . $num;
244     }
245
246     public function getType($content, $returnError = false)
247     {
248         if (getenv('PATH') == '') {
249             //php-fpm does not fill $PATH by default
250             // we have to work around that since System::which() uses it
251             putenv('PATH=/usr/local/bin:/usr/bin:/bin');
252         }
253
254         $tmp = tempnam(sys_get_temp_dir(), 'phorkie-autodetect-');
255         file_put_contents($tmp, $content);
256         $type = Tool_MIME_Type_PlainDetect::autoDetect($tmp);
257         unlink($tmp);
258
259         if ($returnError && $type instanceof \PEAR_Error) {
260             return $type;
261         }
262
263         return $this->findExtForType($type);
264     }
265
266     protected function findExtForType($type)
267     {
268         $ext = 'txt';
269         foreach ($GLOBALS['phorkie']['languages'] as $lext => $arLang) {
270             if ($arLang['mime'] == $type) {
271                 $ext = $lext;
272                 break;
273             }
274         }
275         return $ext;
276     }
277 }
278
279 ?>