diff --git a/files_opds/admin.php b/files_opds/admin.php
new file mode 100644
index 0000000..4bfc869
--- /dev/null
+++ b/files_opds/admin.php
@@ -0,0 +1,30 @@
+ Config::getPreview('OC\Preview\Epub') ? 1 : 0 ],
+ ["pdf" => Config::getPreview('OC\Preview\PDF') ? 1 : 0],
+ ["openoffice" => Config::getPreview('OC\Preview\OpenOffice') ? 1 : 0],
+ ["msoffice" => Config::getPreview('OC\Preview\MSOfficeDoc') ? 1 : 0]
+ );
+
+$tmpl = new \OCP\Template('files_opds', 'admin');
+$tmpl->assign('previewFormats', $formats);
+$tmpl->assign('cover-x', Config::getApp('cover-x', '200'));
+$tmpl->assign('cover-y', Config::getApp('cover-y', '200'));
+$tmpl->assign('thumb-x', Config::getApp('thumb-x', '36'));
+$tmpl->assign('thumb-y', Config::getApp('thumb-y', '36'));
+
+return $tmpl->fetchPage();
diff --git a/files_opds/ajax/admin.php b/files_opds/ajax/admin.php
new file mode 100644
index 0000000..caaadf1
--- /dev/null
+++ b/files_opds/ajax/admin.php
@@ -0,0 +1,54 @@
+ array('message'=> $l->t('Settings updated successfully.'))
+ )
+);
+
+exit();
+
diff --git a/files_opds/appinfo/app.php b/files_opds/appinfo/app.php
index 2ba7431..a157b70 100644
--- a/files_opds/appinfo/app.php
+++ b/files_opds/appinfo/app.php
@@ -2,6 +2,14 @@
$l = OC_L10N::get('files_opds');
+require 'files_opds/lib/epub-preview.php';
+
\OCP\App::registerPersonal('files_opds', 'personal');
+\OCP\App::registerAdmin('files_opds', 'admin');
+/* enable preview providers... */
+// \OCA\Files_Opds\Config::setPreview('OC\Preview\Epub',true);
+
+/* ..and register preview provider */
+\OC\Preview::registerProvider('OC\Preview\Epub');
diff --git a/files_opds/appinfo/info.xml b/files_opds/appinfo/info.xml
index df6b1ae..c82b8d8 100644
--- a/files_opds/appinfo/info.xml
+++ b/files_opds/appinfo/info.xml
@@ -4,7 +4,7 @@
OPDS catalog
Personal OPDS catalog
AGPL
- 0.1.2
+ 0.2
Frank de Lange
7.0
true
diff --git a/files_opds/css/settings.css b/files_opds/css/settings.css
new file mode 100644
index 0000000..54893a9
--- /dev/null
+++ b/files_opds/css/settings.css
@@ -0,0 +1,7 @@
+#opds .indent {
+ padding-left: 1em;
+}
+
+#opds .double-indent {
+ padding-left: 2em;
+}
diff --git a/files_opds/js/admin.js b/files_opds/js/admin.js
new file mode 100644
index 0000000..3aaa0aa
--- /dev/null
+++ b/files_opds/js/admin.js
@@ -0,0 +1,53 @@
+$(document).ready(function(){
+ // save settings
+ var opdsAdminSettings = {
+ save : function() {
+ var epub = document.getElementById('opds-preview-epub').checked ? 'true' : 'false';
+ var pdf = document.getElementById('opds-preview-pdf').checked ? 'true' : 'false';
+ var openoffice = document.getElementById('opds-preview-openoffice').checked ? 'true' : 'false';
+ var msoffice = document.getElementById('opds-preview-msoffice').checked ? 'true' : 'false';
+ var data = {
+ opdsPreviewEpub : epub,
+ opdsPreviewPdf : pdf,
+ opdsPreviewOpenOffice : openoffice,
+ opdsPreviewMsOffice : msoffice
+ };
+ OC.msg.startSaving('#opds-admin .msg');
+ $.post(OC.filePath('files_opds', 'ajax', 'admin.php'), data, opdsAdminSettings.afterSave);
+ },
+ afterSave : function(data){
+ OC.msg.finishedSaving('#opds .msg', data);
+ }
+ };
+
+ var opdsAdminCoverSettings = {
+ save : function() {
+ var data = {
+ opdsCoverX : $('#opds-cover-x').val(),
+ opdsCoverY : $('#opds-cover-y').val(),
+ opdsThumbX : $('#opds-thumb-x').val(),
+ opdsThumbY : $('#opds-thumb-y').val()
+ };
+ OC.msg.startSaving('#opds-admin .msg');
+ $.post(OC.filePath('files_opds', 'ajax', 'admin.php'), data, opdsAdminCoverSettings.afterSave);
+ },
+ afterSave : function(data){
+ OC.msg.finishedSaving('#opds .msg', data);
+ }
+ };
+
+ $('#opds-preview-epub').on("change", opdsAdminSettings.save);
+ $('#opds-preview-pdf').on("change", opdsAdminSettings.save);
+ $('#opds-preview-openoffice').on("change", opdsAdminSettings.save);
+ $('#opds-preview-msoffice').on("change", opdsAdminSettings.save);
+
+ $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y').blur(opdsAdminCoverSettings.save);
+ $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y').keypress(function( event ) {
+ if (event.which == 13) {
+ event.preventDefault();
+ opdsAdminCoverSettings.save();
+ }
+ });
+
+});
+
diff --git a/files_opds/lib/config.php b/files_opds/lib/config.php
index 8b1a716..6e84a54 100644
--- a/files_opds/lib/config.php
+++ b/files_opds/lib/config.php
@@ -38,4 +38,74 @@ class Config
public static function set($key, $value) {
return \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_opds', $key, $value);
}
+
+ /**
+ * @brief get app config value
+ *
+ * @param string $key value to retrieve
+ * @param string $default default value to use
+ * @return string retrieved value or default
+ */
+ public static function getApp($key, $default) {
+ return \OCP\Config::getAppValue('files_opds', $key, $default);
+ }
+
+ /**
+ * @brief set app config value
+ *
+ * @param string $key key for value to change
+ * @param string $value value to use
+ * @return bool success
+ */
+ public static function setApp($key, $value) {
+ return \OCP\Config::setAppValue('files_opds', $key, $value);
+ }
+
+ /**
+ * @brief get preview status
+ *
+ * @param string format
+ * @return bool (true = enabled, false = disabled)
+ */
+ public static function getPreview($format) {
+ $enablePreviewProviders = \OCP\Config::getSystemValue('enabledPreviewProviders', null);
+ if (!($enablePreviewProviders === null)) {
+ return in_array($format, $enablePreviewProviders);
+ }
+ return false;
+ }
+
+ /**
+ * @brief enable/disable preview for selected format
+ *
+ * @param string format
+ * @param bool enable (true = enable, false = disable, default = false)
+ * @return bool
+ */
+ public static function setPreview($format, $enable = 'false') {
+ $enablePreviewProviders = \OCP\Config::getSystemValue('enabledPreviewProviders', null);
+ if ($enable == 'true') {
+ if ($enablePreviewProviders === null) {
+ // set up default providers
+ $enablePreviewProviders = array();
+ array_push($enablePreviewProviders,
+ 'OC\Preview\Image',
+ 'OC\Preview\MP3',
+ 'OC\Preview\TXT',
+ 'OC\Preview\MarkDown');
+ }
+ if (!(in_array($format,$enablePreviewProviders))) {
+ array_push($enablePreviewProviders, $format);
+ }
+ } else {
+ if (!($enablePreviewProviders == null)) {
+ $enablePreviewProviders = array_diff($enablePreviewProviders, array($format));
+ }
+ }
+
+ if (!(\OCP\Config::setSystemValue('enabledPreviewProviders', $enablePreviewProviders))) {
+ logWarn("Failed to enable " . $format . " preview provider (config.php readonly?)");
+ return true;
+ }
+ }
}
diff --git a/files_opds/lib/epub-preview.php b/files_opds/lib/epub-preview.php
new file mode 100644
index 0000000..1bd0164
--- /dev/null
+++ b/files_opds/lib/epub-preview.php
@@ -0,0 +1,48 @@
+getFileInfo($path);
+ if(!$fileInfo) {
+ return false;
+ }
+
+ $absPath = $fileview->toTmpFile($path);
+
+ $epub = new \OCA\Files_Opds\Epub($absPath);
+
+ $cover = $epub->Cover();
+
+ if ($cover) {
+
+ $image = new \OC_Image();
+
+ $image->loadFromData($cover['data']);
+ }
+
+ return (($cover !== null) && $image->valid()) ? $image : false;
+ }
+
+}
+
diff --git a/files_opds/lib/epub.php b/files_opds/lib/epub.php
new file mode 100644
index 0000000..afb099b
--- /dev/null
+++ b/files_opds/lib/epub.php
@@ -0,0 +1,332 @@
+file = $file;
+ $zip = new \ZipArchive();
+ if(!($zip->open($this->file))){
+ \OC_Log::write('epub', "Failed to read epub file", \OC_Log::ERROR);
+ return false;
+ }
+
+ // read container data
+ $data = $zip->getFromName('META-INF/container.xml');
+ if($data == false){
+ \OC_Log::write('epub', "Failed to access epub container data", \OC_Log::ERROR);
+ return false;
+ }
+
+ $xml = new DOMDocument();
+ $xml->registerNodeClass('DOMElement','\OCA\Files_Opds\EPubDOMElement');
+ $xml->loadXML($data);
+ $xpath = new EPubDOMXPath($xml);
+ $nodes = $xpath->query('//n:rootfiles/n:rootfile[@media-type="application/oebps-package+xml"]');
+ $this->meta = $nodes->item(0)->attr('full-path');
+
+ // load metadata
+ $data = $zip->getFromName($this->meta);
+ if(!$data){
+ \OC_Log::write('epub', 'Failed to access epub metadata', \OC_Log::ERROR);
+ return false;
+ }
+
+ $this->xml = new \DOMDocument();
+ $this->xml->registerNodeClass('DOMElement','\OCA\Files_Opds\EPubDOMElement');
+ $this->xml->loadXML($data);
+ $this->xml->formatOutput = true;
+ $this->xpath = new EPubDOMXPath($this->xml);
+
+ $zip->close();
+ }
+
+ /**
+ * @brief file name getter
+ * @return string filename
+ */
+ public static function file() {
+ return $this->file;
+ }
+
+ /**
+ * @brief get author(s)
+ *
+ * @return array $authors
+ */
+ public static function Authors() {
+ // read current data
+ $rolefix = false;
+ $authors = array();
+ $nodes = $this->xpath->query('//opf:metadata/dc:creator[@opf:role="aut"]');
+ if($nodes->length == 0){
+ // no nodes where found, let's try again without role
+ $nodes = $this->xpath->query('//opf:metadata/dc:creator');
+ $rolefix = true;
+ }
+ foreach($nodes as $node){
+ $name = $node->nodeValue;
+ $as = $node->attr('opf:file-as');
+ if(!$as){
+ $as = $name;
+ $node->attr('opf:file-as',$as);
+ }
+ if($rolefix){
+ $node->attr('opf:role','aut');
+ }
+ $authors[$as] = $name;
+ }
+ return $authors;
+ }
+
+ /**
+ * @brief get book title
+ *
+ * @param string $title
+ */
+ public function Title(){
+ return $this->get('dc:title');
+ }
+
+ /**
+ * @brief get language
+ *
+ * @param string $lang
+ */
+ public function Language(){
+ return $this->get('dc:language');
+ }
+
+ /**
+ * @brief get publisher info
+ *
+ * @return string $publisher
+ */
+ public function Publisher(){
+ return $this->get('dc:publisher');
+ }
+
+ /**
+ * @brief get copyright info
+ *
+ * @return string $rights
+ */
+ public function Copyright(){
+ return $this->get('dc:rights');
+ }
+
+ /**
+ * @brief get description
+ *
+ * @return string $description
+ */
+ public function Description(){
+ return $this->get('dc:description');
+ }
+
+ /**
+ * @brief get ISBN number
+ *
+ * @return string $isbn
+ */
+ public function ISBN(){
+ return $this->get('dc:identifier','opf:scheme','ISBN');
+ }
+
+ /**
+ * @brief get Google Books ID
+ *
+ * @return string $google
+ */
+ public function Google(){
+ return $this->get('dc:identifier','opf:scheme','GOOGLE');
+ }
+
+ /**
+ * @brief get Amazon ID
+ *
+ * @return string $amazon
+ */
+ public function Amazon(){
+ return $this->get('dc:identifier','opf:scheme','AMAZON');
+ }
+
+ /**
+ * @brief get subjects (aka. tags)
+ *
+ * @return array $subjects
+ */
+ public function Subjects(){
+ $subjects = array();
+ $nodes = $this->xpath->query('//opf:metadata/dc:subject');
+ foreach($nodes as $node){
+ $subjects[] = $node->nodeValue;
+ }
+ return $subjects;
+ }
+
+ /**
+ * @brief get cover data
+ *
+ * Returns an associative array with the following keys:
+ *
+ * mime - filetype (usually image/jpeg)
+ * data - binary image data
+ * found - internal path, or false if no image is set in epub
+ *
+ * @return array or null
+ */
+ public function Cover(){
+ $nodes = $this->xpath->query('//opf:metadata/opf:meta[@name="cover"]');
+ if($nodes->length) {
+ $coverid = (String) $nodes->item(0)->attr('opf:content');
+ if ($coverid) {
+ $nodes = $this->xpath->query('//opf:manifest/opf:item[@id="'.$coverid.'"]');
+ if ($nodes->length) {
+ $mime = $nodes->item(0)->attr('opf:media-type');
+ $path = $nodes->item(0)->attr('opf:href');
+ $path = dirname('/'.$this->meta).'/'.$path; // image path is relative to meta file
+ $path = ltrim($path,'/');
+ $zip = new \ZipArchive();
+ if($zip->open($this->file)){
+ $data = $zip->getFromName($path);
+ return array(
+ 'mime' => $mime,
+ 'data' => $data,
+ 'found' => $path
+ );
+ }
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @brief simple getter for simple meta attributes
+ *
+ * It should only be used for attributes that are expected to be unique
+ *
+ * @param string $item XML node to get
+ * @param string $att Attribute name
+ * @param string $aval Attribute value
+ * @return string node value
+ */
+ protected function get($item, $att=false, $aval=false){
+ $xpath = '//opf:metadata/'.$item;
+ if ($att) {
+ $xpath .= "[@$att=\"$aval\"]";
+ }
+
+ $nodes = $this->xpath->query($xpath);
+ if($nodes->length){
+ return $nodes->item(0)->nodeValue;
+ }else{
+ return '';
+ }
+ }
+}
+
+class EPubDOMXPath extends DOMXPath {
+ public function __construct(DOMDocument $doc){
+ parent::__construct($doc);
+
+ if(is_a($doc->documentElement, '\OCA\Files_Opds\EPubDOMElement')){
+ foreach($doc->documentElement->namespaces as $ns => $url){
+ $this->registerNamespace($ns,$url);
+ }
+ }
+ }
+}
+
+class EPubDOMElement extends DOMElement {
+ public $namespaces = array(
+ 'n' => 'urn:oasis:names:tc:opendocument:xmlns:container',
+ 'opf' => 'http://www.idpf.org/2007/opf',
+ 'dc' => 'http://purl.org/dc/elements/1.1/'
+ );
+
+
+ public function __construct($name, $value='', $namespaceURI=''){
+ list($ns,$name) = $this->splitns($name);
+ $value = htmlspecialchars($value);
+ if(!$namespaceURI && $ns){
+ $namespaceURI = $this->namespaces[$ns];
+ }
+ parent::__construct($name, $value, $namespaceURI);
+ }
+
+ /**
+ * @brief split given name in namespace prefix and local part
+ *
+ * @param string $name
+ * @return array (namespace, name)
+ */
+ public function splitns($name){
+ $list = explode(':',$name,2);
+ if(count($list) < 2) array_unshift($list,'');
+ return $list;
+ }
+
+ /**
+ * @brief simple EPub namespace aware attribute getter
+ *
+ * @param string attribute
+ * @return string attribute value
+ */
+ public function attr($attr){
+ list($ns,$attr) = $this->splitns($attr);
+
+ $nsuri = '';
+ if($ns){
+ $nsuri = $this->namespaces[$ns];
+ if(!$this->namespaceURI){
+ if($this->isDefaultNamespace($nsuri)){
+ $nsuri = '';
+ }
+ }elseif($this->namespaceURI == $nsuri){
+ $nsuri = '';
+ }
+ }
+
+ if($nsuri){
+ return $this->getAttributeNS($nsuri,$attr);
+ }else{
+ return $this->getAttribute($attr);
+ }
+ }
+}
+
+
+
diff --git a/files_opds/lib/files.php b/files_opds/lib/files.php
index dbae3bf..cfa57ff 100644
--- a/files_opds/lib/files.php
+++ b/files_opds/lib/files.php
@@ -18,7 +18,7 @@ namespace OCA\Files_Opds;
class Files extends \OCA\Files\Helper
{
/**
- * Formats the file info to be returned as OPDS to the client.
+ * Formats the file info to be returned as OPDS to the client
*
* @param \OCP\Files\FileInfo $i
* @return array formatted file info
@@ -28,11 +28,16 @@ class Files extends \OCA\Files\Helper
$entry['id'] = $i['fileid'];
$entry['mtime'] = $i['mtime'] * 1000;
- $entry['icon'] = self::determineIcon($i);
$entry['name'] = $i->getName();
- $entry['mimetype'] = $i['mimetype'];
- $entry['size'] = $i['size'];
$entry['type'] = $i['type'];
+ if ($i['type'] === 'file') {
+ $entry['mimetype'] = $i['mimetype'];
+ $entry['preview'] = self::getPreview($i);
+ $entry['thumbnail'] = self::getThumbnail($i);
+ $entry['humansize'] = \OC_Helper::humanFileSize($i['size']);
+ } else {
+ $entry['icon'] = self::determineIcon($i);
+ }
return $entry;
}
@@ -54,6 +59,33 @@ class Files extends \OCA\Files\Helper
return $files;
}
+ /**
+ * @brief get preview for file
+ * @param \OCP\Files\FileInfo $i
+ * @return string preview URL
+ */
+ public static function getPreview($i) {
+ if (\OC::$server->getPreviewManager()->isMimeSupported($i['mimetype'])) {
+ return \OC_Helper::linkToRoute( 'core_ajax_preview', array('x' => Config::getApp('cover-x', '200'), 'y' => Config::getApp('cover-y', '200'), 'file' => \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getPath($i['fileid']))));
+ } else {
+ return self::determineIcon($i);
+ }
+ }
+
+
+ /**
+ * @brief get thumbnail for file
+ * @param \OCP\Files\FileInfo $i
+ * @return string preview URL
+ */
+ public static function getThumbnail($i) {
+ if (\OC::$server->getPreviewManager()->isMimeSupported($i['mimetype'])) {
+ return \OC_Helper::linkToRoute( 'core_ajax_preview', array('x' => Config::getApp('thumb-x', '36'), 'y' => Config::getApp('thumb-y', '36'), 'file' => \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getPath($i['fileid']))));
+ } else {
+ return self::determineIcon($i);
+ }
+ }
+
/*
* @brief check if $child is a subdirectory of $parent
*
diff --git a/files_opds/templates/admin.php b/files_opds/templates/admin.php
new file mode 100644
index 0000000..0ac3a99
--- /dev/null
+++ b/files_opds/templates/admin.php
@@ -0,0 +1,48 @@
+ $enabled) {
+ echo '' : '>');
+ echo '';
+ }
+}
+
+?>
+
+
+
t('OPDS')); ?>
+
Enable preview for:
+
+
+
+
Cover size
+
+
" value="" />
+
+
" value="" />
+
+
+
Cover thumbnail size
+
+
" value="" />
+
+
" value="" />
+
+
+
diff --git a/files_opds/templates/feed.php b/files_opds/templates/feed.php
index d2f0e6b..265619f 100644
--- a/files_opds/templates/feed.php
+++ b/files_opds/templates/feed.php
@@ -10,11 +10,19 @@
* later.
*/
+
+function formatMetadata($humansize,$mimetype,$name) {
+ return "Size: " . $humansize . "\n"
+ . "Type: " . $mimetype . "\n"
+ . "Filename: " . $name;
+}
+
echo '';
?>
id:
t("%s's library", array($_['user']))); ?>
@@ -54,19 +62,20 @@ echo '';
id:
+
-
-
-
+
@@ -89,19 +98,20 @@ echo '';
id:
+
-
-
-
+
diff --git a/files_opds/templates/personal.php b/files_opds/templates/personal.php
index 22fd980..ef92218 100644
--- a/files_opds/templates/personal.php
+++ b/files_opds/templates/personal.php
@@ -12,7 +12,7 @@
?>
-