'tpl' => __DIR__ . '/templates/',
'css' => 'http://twitter.github.com/bootstrap/assets/css/bootstrap.css',
);
+$GLOBALS['phorkie']['tools'] = array(
+ '\\phorkie\\Tool_Xmllint'
+);
/**
* Array of supported file types / languages.
* Key is the file extension
--- /dev/null
+<div class="file">
+ <div class="header">
+ <a class="btn btn-mini" href="{{file.getLink('raw')}}" style="float: right;">raw</a>
+ {% for toolinfo in file.getToolInfos %}
+ <a class="btn btn-mini" href="{{toolinfo.getLink(file)}}" style="float: right;">{{toolinfo.getTitle}}</a>
+ {% endfor %}
+ <h3 id="{{file.getFilename}}">{{file.getFilename}}<a class="anchorlink" href="#{{file.getFilename}}"></a></h3>
+ </div>
+ <div class="code">
+ {{file.getHighlightedContent|raw}}
+ </div>
+</div>
--- /dev/null
+<div class="row-fluid" style="margin-top: 5ex">
+ <div class="span12" style="text-align: right;">
+ <a class="btn" href="{{repo.getLink('delete')}}">
+ <i class="icon-trash"></i> Delete
+ </a>
+ </div>
+</div>
--- /dev/null
+<h1>{{repo.getDescription}}</h1>
+<div class="row-fluid repo-info">
+ <div class="span4">
+ <a class="btn" href="{{repo.getLink('edit')}}"><i class="icon-edit"></i> edit</a>
+ </div>
+ <div class="span4" style="text-align: center">
+ <h3>Paste #{{repo.id}}</h3>
+ </div>
+ <div class="span4" style="text-align: right">
+ <form method="post" action="{{repo.getLink('fork')}}">
+ <button type="submit" class="btn"><i class="icon-share"></i> fork</button>
+ </form>
+ </div>
+</div>
+
+{% if repo.getCloneURL(true) or repo.getCloneURL(false) %}
+<div class="well">
+ {% if repo.getCloneURL(true) %}
+ <div class="row-fluid">
+ <div class="span3">Public clone URL</div>
+ <div class="span9">
+ <a href="{{repo.getCloneURL(true)}}">{{repo.getCloneURL(true)}}</a>
+ </div>
+ </div>
+ {% endif %}
+ {% if repo.getCloneURL(false) %}
+ <div class="row-fluid">
+ <div class="span3">Private clone URL</div>
+ <div class="span9">
+ <a href="{{repo.getCloneURL(false)}}">{{repo.getCloneURL(false)}}</a>
+ </div>
+ </div>
+ {% endif %}
+</div>
+{% endif %}
{% endblock %}
{% block content %}
-<h1>{{repo.getDescription}}</h1>
-<div class="row-fluid">
- <div class="span4">
- <a class="btn" href="{{repo.getLink('edit')}}"><i class="icon-edit"></i> edit</a>
- </div>
- <div class="span4" style="text-align: center">
- <h3>Paste #{{repo.id}}</h3>
- </div>
- <div class="span4" style="text-align: right">
- <form method="post" action="{{repo.getLink('fork')}}">
- <button type="submit" class="btn"><i class="icon-share"></i> fork</button>
- </form>
- </div>
-</div>
-
-{% if repo.getCloneURL(true) or repo.getCloneURL(false) %}
-<div class="well">
- {% if repo.getCloneURL(true) %}
- <div class="row-fluid">
- <div class="span3">Public clone URL</div>
- <div class="span9">
- <a href="{{repo.getCloneURL(true)}}">{{repo.getCloneURL(true)}}</a>
- </div>
- </div>
- {% endif %}
- {% if repo.getCloneURL(false) %}
- <div class="row-fluid">
- <div class="span3">Private clone URL</div>
- <div class="span9">
- <a href="{{repo.getCloneURL(false)}}">{{repo.getCloneURL(false)}}</a>
- </div>
- </div>
- {% endif %}
-</div>
-{% endif %}
+ {% include 'display-head.htm' %}
{% for file in repo.getFiles %}
-<div class="file">
- <div class="header">
- <a class="btn btn-mini" href="{{file.getLink('raw')}}" style="float: right;">raw</a>
- <h3 id="{{file.getFilename}}">{{file.getFilename}}<a class="anchorlink" href="#{{file.getFilename}}"></a></h3>
- </div>
- <div class="code">
- {{file.getHighlightedContent|raw}}
- </div>
-</div>
+ {% include 'display-file.htm' %}
{% endfor %}
-<div class="row-fluid" style="margin-top: 5ex">
- <div class="span12" style="text-align: right;">
- <a class="btn" href="{{repo.getLink('delete')}}">
- <i class="icon-trash"></i> Delete
- </a>
- </div>
-</div>
+ {% include 'display-foot.htm' %}
{% endblock %}
{% block sidebar %}
--- /dev/null
+{% extends "base.htm" %}
+{% block title %}
+ Tool results:
+ {%if repo.getDescription %}
+ {{repo.getDescription}}
+ {%else%}
+ {{repo.id}}
+ {%endif%}
+{% endblock %}
+
+{% block content %}
+<h1>Tool results: {{repo.getDescription}}</h1>
+<div class="row-fluid repo-info">
+ <div class="span4">
+ <a class="btn" href="{{repo.getLink('edit')}}"><i class="icon-edit"></i> edit</a>
+ <a class="btn" href="{{repo.getLink('display')}}"><i class="icon-arrow-left"></i> back</a>
+ </div>
+ <div class="span4" style="text-align: center">
+ <h3>Paste #{{repo.id}}</h3>
+ </div>
+</div>
+
+ {% for line in toolres.annotations.general %}
+ <div class="alert {{line.getAlertLevel}}">
+ {{line.message}}
+ </div>
+ {% endfor %}
+
+ {% include 'display-file.htm' %}
+
+ {% for number,lineinfos in toolres.annotations if number != 'general' %}
+ {% for line in lineinfos %}
+ <div class="alert {{line.getAlertLevel}}">
+ Line #{{number}}: {{line.message}}
+ </div>
+ {% endfor %}
+ {% endfor %}
+
+ {% include 'display-foot.htm' %}
+{% endblock %}
+
+{% block sidebar %}
+sidebar FIXME
+{% endblock %}
*
* @param string $type Link type. Supported are:
* - "raw"
- * - "display"
+ * - "tool"
+ * @param string $option
*
* @return string
*/
- public function getLink($type)
+ public function getLink($type, $option = null)
{
if ($type == 'raw') {
return '/' . $this->repo->id . '/raw/' . $this->getFilename();
+ } else if ($type == 'tool') {
+ return '/' . $this->repo->id . '/tool/' . $option . '/' . $this->getFilename();
}
throw new Exception('Unknown type');
}
}
return $GLOBALS['phorkie']['languages'][$ext]['mime'];
}
+
+ /**
+ * @return array Array of Tool_Info objects
+ */
+ public function getToolInfos()
+ {
+ $tm = new Tool_Manager();
+ return $tm->getSuitable($this);
+ }
}
?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace phorkie;
+
+class Tool_Info
+{
+ public $class;
+
+ public function __construct($class)
+ {
+ $this->class = $class;
+ }
+
+ public function getLink(File $file)
+ {
+ return $file->getLink('tool', $this->stripPrefix($this->class));
+ }
+
+ public function getTitle()
+ {
+ return $this->stripPrefix($this->class);
+ }
+
+ protected function stripPrefix($class)
+ {
+ $prefix = '\\phorkie\\Tool_';
+ if (substr($class, 0, strlen($prefix)) === $prefix) {
+ return substr($class, strlen($prefix));
+ }
+ return $class;
+ }
+}
+
+?>
--- /dev/null
+<?php
+namespace phorkie;
+
+
+class Tool_Manager
+{
+ public function getSuitable(File $file)
+ {
+ $ext = $file->getExt();
+ $suitables = array();
+ foreach ($GLOBALS['phorkie']['tools'] as $class) {
+ if (array_search($ext, $class::$arSupportedExtensions) !== false) {
+ $suitables[] = new Tool_Info($class);
+ }
+ }
+ return $suitables;
+ }
+
+ /**
+ * Returns the class name from a tool name
+ *
+ * @param string $name Full class name or short name without
+ * 'phorkie\\Tool_' prefix
+ *
+ * @return string Class name or NULL if not found
+ */
+ public function getClass($name)
+ {
+ if (strpos($name, '\\') === false && strpos($name, '_') === false) {
+ return '\\phorkie\\Tool_' . $name;
+ }
+ return $name;
+ }
+
+ public function loadTool($name)
+ {
+ $class = $this->getClass($name);
+ if (!class_exists($class, true)) {
+ throw new Exception('Tool does not exist: ' . $class);
+ }
+
+ return new $class();
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace phorkie;
+
+class Tool_Result
+{
+ public $annotations;
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace phorkie;
+
+class Tool_Result_Line
+{
+ public $message;
+ public $level;
+
+ public function __construct($message, $level = 'ok')
+ {
+ $this->message = $message;
+ $this->setLevel($level);
+ }
+
+ public function setLevel($level)
+ {
+ if ($level !== 'ok' && $level !== 'error' && $level !== 'warning') {
+ throw new Exception('Invalid result line level: ' . $level);
+ }
+ $this->level = $level;
+ }
+
+ public function getAlertLevel()
+ {
+ static $map = array(
+ 'error' => 'alert-error',
+ 'ok' => 'alert-success',
+ 'warning' => '',
+ );
+ return $map[$this->level];
+ }
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+namespace phorkie;
+
+class Tool_Xmllint
+{
+ public static $arSupportedExtensions = array(
+ 'htm', 'html', 'xml'
+ );
+
+ public function run(File $file)
+ {
+ $fpath = $file->getPath();
+ $fpathlen = strlen($fpath);
+
+ $res = new Tool_Result();
+ $cmd = 'xmllint --noout ' . escapeshellarg($fpath) . ' 2>&1';
+ exec($cmd, $output, $retval);
+ if ($retval == 0) {
+ $res->annotations['general'][] = new Tool_Result_Line(
+ 'XML is well-formed', 'ok'
+ );
+ return $res;
+ }
+
+ for ($i = 0; $i < count($output); $i += 3) {
+ $line = $output[$i];
+ if (substr($line, 0, $fpathlen) != $fpath) {
+ throw new Exception('xmllint does not behave as expected: ' . $line);
+ }
+ list($line, $msg) = explode(':', substr($line, $fpathlen + 1), 2);
+ $res->annotations[$line][] = new Tool_Result_Line(
+ $msg, 'error'
+ );
+ }
+
+ $res->annotations['general'][] = new Tool_Result_Line(
+ 'XML is not well-formed', 'error'
+ );
+
+ return $res;
+ }
+}
+
+?>
\ No newline at end of file
RewriteRule ^([0-9]+)/edit$ /edit.php?id=$1
RewriteRule ^([0-9]+)/fork$ /fork.php?id=$1
RewriteRule ^([0-9]+)/raw/(.+)$ /raw.php?id=$1&file=$2
+RewriteRule ^([0-9]+)/tool/([^/]+)/(.+)$ /tool.php?id=$1&tool=$2&file=$3
RewriteRule ^list$ /list.php
RewriteRule ^list/([0-9]+)$ /list.php?page=$1
color: #FFA;
}
-.file {
- margin-top: 2em;
+h1 {
+ margin-bottom: 0.5ex;
+}
+.repo-info {
+ margin-bottom: 2em;
}
.file .header {
padding: 1.0ex;
--- /dev/null
+<?php
+/**
+ * Runs a tool on a file
+ */
+namespace phorkie;
+require_once 'www-header.php';
+$repo = new Repository();
+$repo->loadFromRequest();
+
+if (!isset($_GET['file']) || $_GET['file'] == '') {
+ throw new Exception_Input('File name missing');
+}
+$file = $repo->getFileByName($_GET['file']);
+
+if (!isset($_GET['tool']) || $_GET['tool'] == '') {
+ throw new Exception_Input('Tool name missing');
+}
+
+$tm = new Tool_Manager();
+$tool = $tm->loadTool($_GET['tool']);
+
+$res = $tool->run($file);
+
+render(
+ 'tool',
+ array(
+ 'repo' => $repo,
+ 'file' => $file,
+ 'toolres' => $res,
+ )
+);
+
+?>
\ No newline at end of file