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