aa1ea4c8b8551f12dee39ddfae14bfcc6c5b6d20
[phorkie.git] / src / phorkie / Repository.php
1 <?php
2 namespace phorkie;
3
4
5 class Repository
6 {
7     /**
8      * Repository ID (number in repositories directory)
9      *
10      * @var integer
11      */
12     public $id;
13
14     /**
15      * Full path to the .git repository
16      *
17      * @var string
18      */
19     public $gitDir;
20
21     /**
22      * Full path to the work tree directory
23      *
24      * @var string
25      */
26     public $workDir;
27
28     /**
29      * Revision of the repository that shall be shown
30      *
31      * @var string
32      */
33     public $hash;
34
35
36
37     /**
38      * Load Repository data from GET-Request
39      *
40      * @return void
41      *
42      * @throws Exception When something is wrong
43      */
44     public function loadFromRequest()
45     {
46         if (!isset($_GET['id'])) {
47             throw new Exception_Input('Paste ID missing');
48         }
49         if (!is_numeric($_GET['id'])) {
50             throw new Exception_Input('Paste ID not numeric');
51         }
52         $this->id = (int)$_GET['id'];
53         $this->loadDirs();
54         $this->loadHash();
55     }
56
57     protected function loadDirs()
58     {
59         $gitDir = $GLOBALS['phorkie']['cfg']['gitdir'] . '/' . $this->id . '.git';
60         if (!is_dir($gitDir)) {
61             throw new Exception_NotFound(
62                 sprintf('Paste %d .git dir not found', $this->id)
63             );
64         }
65         $this->gitDir = $gitDir;
66
67         $workDir = $GLOBALS['phorkie']['cfg']['workdir'] . '/' . $this->id;
68         if (!is_dir($workDir)) {
69             throw new Exception_NotFound(
70                 sprintf('Paste %d work dir not found', $this->id)
71             );
72         }
73         $this->workDir = $workDir;
74     }
75
76     public function loadHash()
77     {
78         if ($this->hash !== null) {
79             return;
80         }
81
82         $output = $this->getVc()->getCommand('log')
83             ->setOption('pretty', 'format:%H')
84             ->setOption('max-count', 1)
85             ->execute();
86         $output = trim($output);
87         if (strlen($output) !== 40) {
88             throw new Exception(
89                 'Loading commit hash failed: ' . $output
90             );
91         }
92         $this->hash = $output;
93     }
94
95     public function loadById($id)
96     {
97         if (!is_numeric($id)) {
98             throw new Exception_Input('Paste ID not numeric');
99         }
100         $this->id = (int)$id;
101         $this->loadDirs();
102         $this->loadHash();
103     }
104
105     public function getVc()
106     {
107         return new \VersionControl_Git($this->gitDir);
108     }
109
110     /**
111      * Loads the list of files in this repository
112      *
113      * @return File[] Array of files
114      */
115     public function getFiles()
116     {
117         $files = glob($this->workDir . '/*');
118         $arFiles = array();
119         foreach ($files as $path) {
120             $arFiles[] = new File($path, $this);
121         }
122         return $arFiles;
123     }
124
125     public function getFileByName($name, $bHasToExist = true)
126     {
127         $base = basename($name);
128         if ($base != $name) {
129             throw new Exception('No directories supported for now');
130         }
131         if ($name == '') {
132             throw new Exception_Input('Empty file name given');
133         }
134         $path = $this->workDir . '/' . $base;
135         if ($bHasToExist && !is_readable($path)) {
136             throw new Exception_Input('File does not exist');
137         }
138         return new File($path, $this);
139     }
140
141     public function hasFile($name)
142     {
143         try {
144             $this->getFileByName($name);
145         } catch (Exception $e) {
146             return false;
147         }
148         return true;
149     }
150
151     /**
152      * Permanently deletes the paste repository without any way to get
153      * it back.
154      *
155      * @return boolean True if all went well, false if not
156      */
157     public function delete()
158     {
159         return Tools::recursiveDelete($this->workDir)
160             && Tools::recursiveDelete($this->gitDir);
161     }
162
163     public function getDescription()
164     {
165         if (!is_readable($this->gitDir . '/description')) {
166             return null;
167         }
168         return file_get_contents($this->gitDir . '/description');
169     }
170
171     public function setDescription($description)
172     {
173         file_put_contents($this->gitDir . '/description', $description);
174     }
175
176     /**
177      * Get a link to the repository
178      *
179      * @param string $type Link type. Supported are:
180      *                     - "commit"
181      *                     - "edit"
182      *                     - "delete"
183      *                     - "delete-confirm"
184      *                     - "display"
185      *                     - "fork"
186      * @param string $option
187      *
188      * @return string
189      */
190     public function getLink($type, $option = null)
191     {
192         if ($type == 'edit') {
193             return '/' . $this->id . '/edit';
194         } else if ($type == 'display') {
195             return '/' . $this->id;
196         } else if ($type == 'fork') {
197             return '/' . $this->id . '/fork';
198         } else if ($type == 'delete') {
199             return '/' . $this->id . '/delete';
200         } else if ($type == 'delete-confirm') {
201             return '/' . $this->id . '/delete/confirm';
202         } else if ($type == 'commit') {
203             return '/' . $this->id . '/' . $option;
204         }
205         throw new Exception('Unknown link type');
206     }
207
208     public function getCloneURL($public = true)
209     {
210         $var = $public ? 'public' : 'private';
211         if (isset($GLOBALS['phorkie']['cfg']['git'][$var])) {
212             return $GLOBALS['phorkie']['cfg']['git'][$var] . $this->id . '.git';
213         }
214         return null;
215     }
216
217     /**
218      * Returns the history of the repository.
219      * We don't use VersionControl_Git's rev list fetcher since it does not
220      * give us separate email addresses and names, and it does not give us
221      * the amount of changed (added/deleted) lines.
222      *
223      * @return array Array of history objects
224      */
225     public function getHistory()
226     {
227         $output = $this->getVc()->getCommand('log')
228             ->setOption('pretty', 'format:commit %H%n%at%n%an%n%ae')
229             ->setOption('max-count', 10)
230             ->setOption('shortstat')
231             ->execute();
232
233         $arCommits = array();
234         $arOutput = explode("\n", $output);
235         $lines = count($arOutput);
236         $current = 0;
237         while ($current < $lines) {
238             $commit = new Repository_Commit();
239             list($name,$commit->hash) = explode(' ', $arOutput[$current]);
240             if ($name !== 'commit') {
241                 throw new Exception(
242                     'Git log output format not as expected: ' . $arOutput[$current]
243                 );
244             }
245             $commit->committerTime  = $arOutput[$current + 1];
246             $commit->committerName  = $arOutput[$current + 2];
247             $commit->committerEmail = $arOutput[$current + 3];
248
249             $arLineParts = explode(' ', trim($arOutput[$current + 4]));
250             $commit->filesChanged = $arLineParts[0];
251             $commit->linesAdded   = $arLineParts[3];
252             $commit->linesDeleted = $arLineParts[5];
253
254             $current += 6;
255
256             $arCommits[] = $commit;
257         }
258
259         return $arCommits;
260     }
261 }
262
263 ?>