8 * Repository ID (number in repositories directory)
15 * Full path to the .git repository
22 * Full path to the work tree directory
29 * Revision of the repository that shall be shown
36 * Commit message of the last (or current) revision
44 * Load Repository data from GET-Request
48 * @throws Exception When something is wrong
50 public function loadFromRequest()
52 if (!isset($_GET['id'])) {
53 throw new Exception_Input('Paste ID missing');
55 if (!is_numeric($_GET['id'])) {
56 throw new Exception_Input('Paste ID not numeric');
58 if (isset($_GET['rev'])) {
59 $this->hash = $_GET['rev'];
62 $this->id = (int)$_GET['id'];
68 public function loadById($id)
70 if (!is_numeric($id)) {
71 throw new Exception_Input('Paste ID not numeric');
78 protected function loadDirs()
80 $gitDir = $GLOBALS['phorkie']['cfg']['gitdir'] . '/' . $this->id . '.git';
81 if (!is_dir($gitDir)) {
82 throw new Exception_NotFound(
83 sprintf('Paste %d .git dir not found', $this->id)
86 $this->gitDir = $gitDir;
88 $workDir = $GLOBALS['phorkie']['cfg']['workdir'] . '/' . $this->id;
89 if (!is_dir($workDir)) {
90 throw new Exception_NotFound(
91 sprintf('Paste %d work dir not found', $this->id)
94 $this->workDir = $workDir;
97 public function loadHash()
99 if ($this->hash !== null) {
103 $output = $this->getVc()->getCommand('log')
104 ->setOption('pretty', 'format:%H')
105 ->setOption('max-count', 1)
107 $output = trim($output);
108 if (strlen($output) !== 40) {
110 'Loading commit hash failed: ' . $output
113 $this->hash = $output;
116 public function reloadHash()
119 return $this->loadHash();
123 * Populates $this->message
127 public function loadMessage()
129 $rev = (isset($this->hash)) ? $this->hash : 'HEAD';
130 $output = $this->getVc()->getCommand('log')
131 ->setOption('oneline')
135 $output = trim($output);
136 if (strpos($output, ' ') > 0) {
137 $output = substr($output, strpos($output, ' '), strlen($output));
138 $this->message = trim($output);
140 $this->message = "This commit message intentionally left blank.";
144 public function getVc()
146 return new \VersionControl_Git($this->gitDir);
150 * Loads the list of files in this repository
152 * @return File[] Array of file objects
154 public function getFiles()
156 $files = $this->getFilePaths();
158 foreach ($files as $name) {
159 $arFiles[] = new File($name, $this);
165 * Decodes unicode characters in git filenames
166 * They begin and end with double quote characters, and may contain
167 * backslash + 3 letter octal code numbers representing the character.
170 * > "t\303\244st.txt"
174 * On the shell, you can pipe them into "printf" and have them decoded.
176 * @param string Encoded git file name
178 * @return string Decoded file name
180 protected function decodeFileName($name)
182 $name = substr($name, 1, -1);
183 $name = str_replace('\"', '"', $name);
184 $name = preg_replace_callback(
187 return chr(octdec(substr($ar[0], 1)));
195 * Return array with all file paths in this repository
199 protected function getFilePaths()
201 if ($this->hash === null) {
206 $output = $this->getVc()->getCommand('ls-tree')
208 ->setOption('name-only')
211 $files = explode("\n", trim($output));
212 foreach ($files as &$file) {
213 if ($file{0} == '"') {
214 $file = $this->decodeFileName($file);
220 public function getFileByName($name, $bHasToExist = true)
222 $name = Tools::sanitizeFilename($name);
224 throw new Exception_Input('Empty file name given');
228 $files = $this->getFilePaths();
229 if (array_search($name, $files) === false) {
230 throw new Exception_Input('File does not exist');
233 return new File($name, $this);
236 public function hasFile($name)
239 $this->getFileByName($name);
240 } catch (Exception $e) {
247 * Permanently deletes the paste repository without any way to get
250 * @return boolean True if all went well, false if not
252 public function delete()
254 $db = new Database();
255 $db->getIndexer()->deleteRepo($this);
257 $bOk = Tools::recursiveDelete($this->workDir)
258 && Tools::recursiveDelete($this->gitDir);
260 $not = new Notificator();
266 public function getTitle()
268 $desc = $this->getDescription();
269 if (trim($desc) != '') {
273 return 'paste #' . $this->id;
276 public function getDescription()
278 if (!is_readable($this->gitDir . '/description')) {
281 return file_get_contents($this->gitDir . '/description');
284 public function setDescription($description)
286 file_put_contents($this->gitDir . '/description', $description);
290 * @return array Array with keys "email" and "name"
292 public function getOwner()
295 $name = $this->getVc()->getCommand('config')
296 ->addArgument('owner.name')->execute();
297 } catch (\VersionControl_Git_Exception $e) {
298 $name = $GLOBALS['phorkie']['auth']['anonymousName'];
301 $email = $this->getVc()->getCommand('config')
302 ->addArgument('owner.email')->execute();
303 } catch (\VersionControl_Git_Exception $e) {
304 $email = $GLOBALS['phorkie']['auth']['anonymousEmail'];
307 return array('name' => trim($name), 'email' => trim($email));
311 * Get a link to the repository
313 * @param string $type Link type. Supported are:
321 * @param string $option Additional link option, e.g. revision number
322 * @param boolean $full Return full URL or normal relative
326 public function getLink($type, $option = null, $full = false)
328 if ($type == 'edit') {
329 $link = $this->id . '/edit';
330 if ($option !== null) {
331 $link .= '/' . urlencode($option);
333 } else if ($type == 'display') {
335 } else if ($type == 'fork') {
336 $link = $this->id . '/fork';
337 } else if ($type == 'doap') {
338 $link = $this->id . '/doap';
339 } else if ($type == 'delete') {
340 $link = $this->id . '/delete';
341 } else if ($type == 'delete-confirm') {
342 $link = $this->id . '/delete/confirm';
343 } else if ($type == 'embed') {
344 $link = $this->id . '/embed';
345 } else if ($type == 'oembed-json') {
346 $link = 'oembed.php?format=json&url='
347 . urlencode($this->getLink('display', null, true));
348 } else if ($type == 'oembed-xml') {
349 $link = 'oembed.php?format=xml&url='
350 . urlencode($this->getLink('display', null, true));
351 } else if ($type == 'remotefork') {
352 return 'web+fork:' . $this->getLink('display', null, true);
353 } else if ($type == 'revision') {
354 $link = $this->id . '/rev/' . $option;
355 } else if ($type == 'linkback') {
356 $link = $this->id . '/linkback';
358 throw new Exception('Unknown link type');
362 $link = Tools::fullUrl($link);
367 public function getCloneURL($public = true)
369 $var = $public ? 'public' : 'private';
370 if (isset($GLOBALS['phorkie']['cfg']['git'][$var])) {
371 return $GLOBALS['phorkie']['cfg']['git'][$var] . $this->id . '.git';
377 * Returns the history of the repository.
378 * We don't use VersionControl_Git's rev list fetcher since it does not
379 * give us separate email addresses and names, and it does not give us
380 * the amount of changed (added/deleted) lines.
382 * @return array Array of history objects
384 public function getHistory()
386 $output = $this->getVc()->getCommand('log')
387 ->setOption('pretty', 'format:commit %H%n%at%n%an%n%ae')
388 ->setOption('max-count', 10)
389 ->setOption('shortstat')
392 $arCommits = array();
393 $arOutput = explode("\n", $output);
394 $lines = count($arOutput);
396 while ($current < $lines) {
397 $commit = new Repository_Commit();
398 list($name,$commit->hash) = explode(' ', $arOutput[$current]);
399 if ($name !== 'commit') {
401 'Git log output format not as expected: ' . $arOutput[$current]
404 $commit->committerTime = $arOutput[$current + 1];
405 $commit->committerName = $arOutput[$current + 2];
406 $commit->committerEmail = $arOutput[$current + 3];
408 if (substr($arOutput[$current + 4], 0, 1) != ' ') {
409 //commit without changed lines
410 $arCommits[] = $commit;
415 $arLineParts = explode(' ', trim($arOutput[$current + 4]));
416 $commit->filesChanged = $arLineParts[0];
417 $commit->linesAdded = $arLineParts[3];
418 if (isset($arLineParts[5])) {
419 $commit->linesDeleted = $arLineParts[5];
424 $arCommits[] = $commit;
431 * @return Repository_ConnectionInfo
433 public function getConnectionInfo()
435 return new Repository_ConnectionInfo($this);