<?xml version="1.0" encoding="UTF-8" ?>
<database>
+ <!-- http://www.wiltonhotel.com/_ext/pear/docs/MDB2/docs/xml_schema_documentation.html -->
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
'name' => 'gui#tag',
'verb' => 'GET',
),
+ array(
+ 'url' => '/note/{guid}',
+ 'name' => 'gui#note',
+ 'verb' => 'GET',
+ ),
array(
'url' => '/tokens',
'name' => 'gui#tokens',
use \OCP\AppFramework\Http\TemplateResponse;
use \OCA\Grauphel\Lib\Client;
use \OCA\Grauphel\Lib\TokenStorage;
+use \OCA\Grauphel\Lib\Response\ErrorResponse;
/**
* Owncloud frontend
return $res;
}
+ /**
+ * Show contents of a note
+ *
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function note($guid)
+ {
+ $res = new TemplateResponse('grauphel', 'gui-note');
+
+ $note = $this->getNotes()->load($guid, false);
+ if ($note === null) {
+ return new ErrorResponse('Note does not exist');
+ }
+
+ $converter = new \OCA\Grauphel\Lib\Converter\Html();
+ $converter->internalLinkHandler = array($this, 'noteLinkHandler');
+ $res->setParams(
+ array(
+ 'note' => $note,
+ 'note-content' => $converter->convert(
+ $note->{'note-content'}
+ ),
+ )
+ );
+
+ $this->addNavigation($res);
+ return $res;
+ }
+
+ public function noteLinkHandler($noteTitle)
+ {
+ $guid = $this->getNotes()->loadGuidByTitle($noteTitle);
+ if ($guid === null) {
+ return '#';
+ }
+ return $this->urlGen->linkToRoute(
+ 'grauphel.gui.note', array('guid' => $guid)
+ );
+ }
+
/**
* Show all notes of a tag
*
public function tag($rawtag)
{
$notes = $this->getNotes()->loadNotesOverview(null, $rawtag);
+ usort(
+ $notes,
+ function($noteA, $noteB) {
+ return strcmp($noteA['title'], $noteB['title']);
+ }
+ );
$res = new TemplateResponse('grauphel', 'tag');
$res->setParams(
a.action {
line-height: 30px;
}
+a.cellclick {
+ display: block;
+}
table.table form {
display: inline;
}
border: none;
background-color: transparent;
}
+
+.muted {
+ color: #999;
+}
+
+.note-content {
+ padding-top: 2ex;
+}
+
+.note-content .strikethrough {
+ text-decoration: line-through;
+}
+.note-content .highlight {
+ background-color: yellow;
+}
+.note-content .small {
+ font-size: 80%;
+}
+.note-content .large {
+ font-size: 150%;
+}
+.note-content .huge {
+ font-size: 200%;
+}
+.note-content a {
+ text-decoration: underline;
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category Tools
+ * @package Grauphel
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @copyright 2014 Christian Weiske
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3
+ * @link http://cweiske.de/grauphel.htm
+ */
+namespace OCA\Grauphel\Lib\Converter;
+use \XMLReader;
+
+/**
+ * Convert Tomboy note XML to HTML
+ *
+ * Tomboy already ships with a converter:
+ * https://git.gnome.org/browse/tomboy/tree/Tomboy/Addins/ExportToHtml/ExportToHtml.xsl
+ * We cannot use it since we want nice callbacks, and we do not want to rely
+ * on the PHP XSL extension, and we have to fix the links.
+ *
+ * @category Tools
+ * @package Grauphel
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @copyright 2014 Christian Weiske
+ * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3
+ * @version Release: @package_version@
+ * @link http://cweiske.de/grauphel.htm
+ */
+class Html
+{
+ protected static $tagMap = array(
+ 'list' => 'ul',
+ 'list-item' => 'li',
+ 'bold' => 'b',
+ 'italic' => 'i',
+ 'monospace' => 'tt',
+ );
+
+ protected static $styleClassMap = array(
+ 'strikethrough' => 'strikethrough',
+ 'highlight' => 'highlight',
+ 'size:small' => 'small',
+ 'size:large' => 'large',
+ 'size:huge' => 'huge',
+ );
+
+ public $internalLinkHandler;
+
+
+
+ public function __construct()
+ {
+ $this->internalLinkHandler = array($this, 'internalLinkHandler');
+ }
+
+ /**
+ * Converts the tomboy note XML into HTML
+ *
+ * @param string $xmlContent Tomboy note content
+ *
+ * @return string HTML
+ */
+ public function convert($xmlContent)
+ {
+ if (strpos($xmlContent, '</link:internal><link:internal>') !== false) {
+ $xmlContent = $this->fixNastyLinks($xmlContent);
+ }
+
+ $html = '';
+ $reader = new XMLReader();
+ $reader->xml(
+ '<?xml version="1.0" encoding="utf-8"?>' . "\n"
+ . '<content xmlns:size="size" xmlns:link="link">'
+ . $xmlContent
+ . '</content>'
+ );
+
+ $withinLink = false;
+ $store = &$html;
+ while ($reader->read()) {
+ switch ($reader->nodeType) {
+ case XMLReader::ELEMENT:
+ //echo $reader->name . "\n";
+ if (isset(static::$tagMap[$reader->name])) {
+ $store .= '<' . static::$tagMap[$reader->name] . '>';
+ } else if (isset(static::$styleClassMap[$reader->name])) {
+ $store .= '<span class="'
+ . static::$styleClassMap[$reader->name]
+ . '">';
+ } else if (substr($reader->name, 0, 5) == 'link:') {
+ $withinLink = true;
+ $linkText = '';
+ $store = &$linkText;
+ }
+ break;
+ case XMLReader::END_ELEMENT:
+ if (isset(static::$tagMap[$reader->name])) {
+ $store .= '</' . static::$tagMap[$reader->name] . '>';
+ } else if (isset(static::$styleClassMap[$reader->name])) {
+ $store .= '</span>';
+ } else if (substr($reader->name, 0, 5) == 'link:') {
+ $withinLink = false;
+ $store = &$html;
+ $linkUrl = htmlspecialchars_decode(strip_tags($linkText));
+ if ($reader->name == 'link:internal') {
+ $linkUrl = call_user_func($this->internalLinkHandler, $linkUrl);
+ } else {
+ $linkUrl = $this->fixLinkUrl($linkUrl);
+ }
+ $store .= '<a href="' . htmlspecialchars($linkUrl) . '">'
+ . $linkText
+ . '</a>';
+ }
+ break;
+ case XMLReader::TEXT:
+ case XMLReader::SIGNIFICANT_WHITESPACE:
+ $store .= nl2br(htmlspecialchars($reader->value));
+ break;
+ default:
+ throw new \Exception(
+ 'Unsupported XML node type: ' . $reader->nodeType
+ );
+ }
+ }
+
+ $html = str_replace("</ul><br />\n", "</ul>\n", $html);
+
+ return $html;
+ }
+
+ /**
+ * Fixes external URLs without a protocol
+ *
+ * @param string $linkUrl URL to fix
+ *
+ * @return string Fixed URL
+ */
+ protected function fixLinkUrl($linkUrl)
+ {
+ if ($linkUrl{0} == '/') {
+ //Unix file path
+ $linkUrl = 'file://' . $linkUrl;
+ }
+ return $linkUrl;
+ }
+
+ /**
+ * Dummy internal link handler that simply adds ".htm" to the note title
+ *
+ * @param string $linkUrl Title of page that is linked
+ *
+ * @return string URL to link to
+ */
+ public function internalLinkHandler($linkUrl)
+ {
+ return $linkUrl . '.htm';
+ }
+
+ /**
+ * Re-arranges the XML of formatted links to that clean link tags can
+ * be generated.
+ *
+ * Tomboy 1.15.2 allows link formatting, and the resulting XML is a
+ * mess of multiple(!) link tags that are within or around other formatting
+ * tags.
+ *
+ * This method tries to re-arrange the links so that only a single link tag
+ * appears with all the formatting inside.
+ *
+ * @param string $xmlContent Tomboy note content
+ *
+ * @return string XML content, with re-arranged link tags.
+ */
+ protected function fixNastyLinks($xmlContent)
+ {
+ preg_match_all(
+ '#(?:<.*>)?<link:internal>.+</link:internal><link:internal>.+</link:internal>#U',
+ $xmlContent,
+ $matches
+ );
+
+ foreach ($matches[0] as $nastyLink) {
+ $cleaner = str_replace('</link:internal><link:internal>', '', $nastyLink);
+ $cleaner = preg_replace('#<([a-z]+)><(link:internal)>#U', '<\2><\1>', $cleaner);
+ $cleaner = preg_replace('#</(link:internal)></([a-z]+)>#U', '</\2></\1>', $cleaner);
+ $cleaner = str_replace('</link:internal><link:internal>', '', $cleaner);
+ $xmlContent = str_replace($nastyLink, $cleaner, $xmlContent);
+ }
+
+ return $xmlContent;
+ }
+}
+?>
return $this->noteFromRow($row);
}
+ /**
+ * Load a GUID of a note by the note title
+ *
+ * @param string $title Note title
+ *
+ * @return string GUID, NULL if note could not be found
+ */
+ public function loadGuidByTitle($title)
+ {
+ $row = \OC_DB::executeAudited(
+ 'SELECT note_guid FROM `*PREFIX*grauphel_notes`'
+ . ' WHERE `note_user` = ? AND `note_title` = ?',
+ array($this->username, $title)
+ )->fetchRow();
+
+ if ($row === false) {
+ return null;
+ }
+
+ return $row['note_guid'];
+ }
+
/**
* Save a note into storage.
*
--- /dev/null
+<link rel="stylesheet" href="<?php p(OCP\Util::linkTo('grauphel','grauphel.css')); ?>" type="text/css"/>
+
+<?php /** @var $l OC_L10N */ ?>
+<?php $_['appNavigation']->printPage(); ?>
+
+<script type="text/javascript" src="<?php p(OCP\Util::linkTo('grauphel','js/grauphel.js')); ?>"></script>
+
+<div id="app-content" class="content">
+ <h1><?php p($_['note']->title); ?></h1>
+ <p class="muted">
+ Last modified:
+ <?php p(\OCP\Util::formatDate(strtotime($_['note']->{'last-change-date'}))); ?>
+ </p>
+ <div class="note-content">
+ <?php echo $_['note-content']; ?>
+ </div>
+</div>
<?php /** @var $l OC_L10N */ ?>
<?php $_['appNavigation']->printPage(); ?>
-<div id="app-content" class="content">
+<div id="app-content" class="list">
<h1>Notebook: <?php p($_['tag']); ?></h1>
- <p class="error">
- Work in progress: You can't do anything here.
- </p>
- <ul>
+
+ <table class="table" id="grauphel-notes">
+ <thead>
+ <tr>
+ <th>Title</th>
+ <th>Last change</th>
+ </tr>
+ </thead>
+ <tbody>
+
<?php foreach ($_['notes'] as $note) { ?>
- <li data-id="<?php p($note['guid']); ?>"><a href="#"><?php p($note['title']); ?></a></li>
+ <tr id="note-<?php p($note['guid']); ?>">
+ <td>
+ <a class="cellclick" href="<?php p(OCP\Util::linkToRoute('grauphel.gui.note', array('guid' => $note['guid']))); ?>"><?php p($note['title']); ?></a>
+ </td>
+ <td>
+ </td>
+ </tr>
<?php } ?>
- </ul>
+
+ </tbody>
+ </table>
</div>
--- /dev/null
+<?php
+require_once __DIR__ . '/../../../lib/converter/html.php';
+
+class Lib_Converter_HtmlTest extends PHPUnit_Framework_TestCase
+{
+ public function testConvert()
+ {
+ $input = file_get_contents(__DIR__ . '/../../data/formattest.tomboynotecontent');
+
+ $converter = new OCA\Grauphel\Lib\Converter\Html();
+ $output = $converter->convert($input);
+ $this->assertEquals(
+ file_get_contents(__DIR__ . '/../../data/formattest.html'),
+ $output
+ );
+ }
+
+ public function testXSS()
+ {
+ $input = file_get_contents(__DIR__ . '/../../data/xss.tomboynotecontent');
+
+ $converter = new OCA\Grauphel\Lib\Converter\Html();
+ $output = $converter->convert($input);
+ $this->assertEquals(
+ file_get_contents(__DIR__ . '/../../data/xss.html'),
+ $output
+ );
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+Eine Zeile Text.<br />
+Zeilenumbruch.<br />
+Noch ein Zeilenumbruch.<br />
+<br />
+Eine Leerzeile obendrüber.<br />
+<br />
+Jetzt kommt eine Liste:<br />
+<ul><li>eins<br />
+</li><li>zwei<br />
+<ul><li>zwei-eins<br />
+<ul><li>zwei-eins-eins<br />
+</li><li>zwei-eins-zwei<br />
+</li></ul></li><li>zwei-zwei<br />
+</li></ul></li><li>drei<br />
+<ul><li>drei-eins<br />
+<ul><li>drei-eins-eins<br />
+<ul><li>drei-eins-eins-eins</li></ul></li></ul></li></ul></li></ul>
+Formatierungen:<br />
+<b>Fetter Text</b><br />
+<i>Kursiver Text</i><br />
+<span class="strikethrough">Durchgestrichener Text</span><br />
+<span class="highlight">Hervorgehobener Text</span><br />
+<tt>Feste Breite</tt><br />
+<br />
+Schriftgrößen:<br />
+<span class="small">klein</span><br />
+normal<br />
+<span class="large">groß</span><br />
+<span class="huge">riesig</span><br />
+<br />
+Links:<br />
+<ul><li><a href="Test.htm">Test</a><br />
+</li><li><a href="http://cweiske.de/?foo#bar">http://cweiske.de/?foo#bar</a><br />
+</li><li><a href="file:///home/cweiske/fam.jpg">/home/cweiske/fam.jpg</a></li></ul>
+<br />
+Ende.<br />
--- /dev/null
+Eine Zeile Text.
+Zeilenumbruch.
+Noch ein Zeilenumbruch.
+
+Eine Leerzeile obendrüber.
+
+Jetzt kommt eine Liste:
+<list><list-item dir="ltr">eins
+</list-item><list-item dir="ltr">zwei
+<list><list-item dir="ltr">zwei-eins
+<list><list-item dir="ltr">zwei-eins-eins
+</list-item><list-item dir="ltr">zwei-eins-zwei
+</list-item></list></list-item><list-item dir="ltr">zwei-zwei
+</list-item></list></list-item><list-item dir="ltr">drei
+<list><list-item dir="ltr">drei-eins
+<list><list-item dir="ltr">drei-eins-eins
+<list><list-item dir="ltr">drei-eins-eins-eins</list-item></list></list-item></list></list-item></list></list-item></list>
+Formatierungen:
+<bold>Fetter Text</bold>
+<italic>Kursiver Text</italic>
+<strikethrough>Durchgestrichener Text</strikethrough>
+<highlight>Hervorgehobener Text</highlight>
+<monospace>Feste Breite</monospace>
+
+Schriftgrößen:
+<size:small>klein</size:small>
+normal
+<size:large>groß</size:large>
+<size:huge>riesig</size:huge>
+
+Links:
+<list><list-item dir="ltr"><link:internal>Test</link:internal>
+</list-item><list-item dir="ltr"><link:url>http://cweiske.de/?foo#bar</link:url>
+</list-item><list-item dir="ltr"><link:url>/home/cweiske/fam.jpg</link:url></list-item></list>
+
+Ende.
--- /dev/null
+<h1>Hallo!</h1><br />
+Was ist denn <a href="foo">bar<br />
+<br />
+Link mit fett drin: <a href="Formattest.htm">Format<b>te</b>st</a>.<br />
+<ul><li>Liste<br />
+</li><li>ein <b>bisschen</b> fett<br />
+</li><li>mo<tt>nospac</tt>ed<br />
+</li><li></li></ul></div></div></div></div></div></li></ul>
+<br />
+Link zum <a href="Formattest.htm"><span class="highlight">Form</span>a<tt>tt</tt>e<span class="huge">st</span></a>.<br />
+Link mit <a href="Sonder">zeichen.htm">Sonder">zeichen</a>.<br />
--- /dev/null
+<h1>Hallo!</h1>
+Was ist denn <a href="foo">bar
+
+Link mit fett drin: <link:internal>Format<bold>te</bold></link:internal><link:internal>st</link:internal>.
+<list><list-item dir="ltr">Liste
+</list-item><list-item dir="ltr">ein <bold>bisschen</bold> fett
+</list-item><list-item dir="ltr">mo<monospace>nospac</monospace>ed
+</list-item><list-item dir="ltr"></li></ul></div></div></div></div></div></list-item></list>
+
+Link zum <highlight><link:internal>Form</link:internal></highlight><link:internal>a<monospace>tt</monospace></link:internal><link:internal>e<size:huge>st</size:huge></link:internal>.
+Link mit <link:internal>Sonder">zeichen</link:internal>.