DaPortal

<?php //$Id$
//Copyright (c) 2015-2016 Pierre Pronchery <khorben@defora.org>
//This file is part of DeforaOS Web DaPortal
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, version 3 of the License.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program. If not, see <http://www.gnu.org/licenses/>.
//ManualModule
class ManualModule extends Module
{
//public
//methods
//essential
//ManualModule::call
public function call(Engine $engine, Request $request, $internal = 0)
{
if(($action = $request->getAction()) === FALSE)
$action = 'default';
if($internal)
switch($action)
{
case 'actions':
return $this->$action($engine,
$request);
default:
return FALSE;
}
switch($action)
{
case 'default':
case 'display':
case 'list':
$action = 'call'.$action;
return $this->$action($engine, $request);
default:
return new ErrorResponse(_('Invalid action'),
Response::$CODE_ENOENT);
}
}
//accessors
//ManualModule::getRequest
public function getRequest($action = FALSE, $parameters = FALSE)
{
$id = FALSE;
$title = FALSE;
if(is_array($parameters))
{
if(isset($parameters['section'])
&& is_numeric($parameters['section']))
{
$id = $parameters['section'];
unset($parameters['section']);
}
if(isset($parameters['page']))
{
$title = $parameters['page'];
unset($parameters['page']);
}
}
return new Request($this->name, $action, $id, $title,
$parameters);
}
//protected
//methods
//accessors
//ManualModule::getPages
protected function getPages(Engine $engine, $name)
{
$ret = array();
if(($path = $this->configGet('path')) === FALSE)
{
$message = 'Path to manual pages not configured';
return $engine->log(LOG_ERR, $message);
}
if(strpos($name, '/') !== FALSE)
return FALSE;
$path = explode(',', $path);
foreach($path as $p)
{
if(($dir = @opendir($p)) === FALSE)
continue;
while(($de = readdir($dir)) !== FALSE)
{
if(!is_dir($p.'/'.$de))
continue;
if(sscanf($de, 'html%s', $section) != 1)
continue;
$filename = $p.'/'.$de.'/'.$name.'.html';
if(($title = $this->_pagesOpen($filename))
=== FALSE)
continue;
$ret[] = array('section' => $section,
'page' => $name, 'title' => $title);
}
closedir($dir);
}
return $ret;
}
private function _pagesOpen($filename)
{
$xml = new DOMDocument();
//we can hide errors
if(@$xml->loadHTMLfile($filename, LIBXML_NOENT) !== TRUE)
return FALSE;
$title = $xml->getElementsByTagName('title');
return ($title->length == 1)
? trim($title->item(0)->textContent) : FALSE;
}
//ManualModule::getSectionPages
protected function getSectionPages(Engine $engine, $section)
{
//XXX code duplication
$ret = array();
if(($path = $this->configGet('path')) === FALSE)
{
$message = 'Path to manual pages not configured';
return $engine->log(LOG_ERR, $message);
}
if(strpos($section, '/') !== FALSE)
return FALSE;
$path = explode(',', $path);
foreach($path as $p)
{
if(($dir = @opendir($p.'/html'.$section)) === FALSE)
continue;
while(($de = readdir($dir)) !== FALSE)
{
if(substr($de, -5) != '.html')
continue;
$filename = $p.'/html'.$section.'/'.$de;
//XXX this is *very* expensive
if(($title = $this->_pagesOpen($filename))
=== FALSE)
continue;
$name = substr($de, 0, -5);
$ret[] = array('section' => $section,
'page' => $name, 'title' => $title);
}
closedir($dir);
}
usort($ret, function($a, $b)
{
return strcmp($a['page'], $b['page']);
});
return $ret;
}
//ManualModule::getSectionDescription
protected function getSectionDescription($section)
{
switch($section)
{
case '1':
return _('General commands (tools and utilities)');
case '2':
return _('System calls and error numbers');
case '3':
return _('System libraries');
case '3f':
return _('System libraries (Fortran)');
case '3lua':
return _('System libraries (Lua)');
case '4':
return _('Kernel interfaces');
case '5':
return _('File formats');
case '6':
return _('Online games');
case '7':
return _('Miscellaneous information pages');
case '8':
return _('System maintenance procedures and commands');
case '9':
return _('Kernel internals');
case '9lua':
return _('Kernel internals (Lua)');
default:
return FALSE;
}
}
//ManualModule::getSections
protected function getSections(Engine $engine)
{
//XXX code duplication
$ret = array();
if(($path = $this->configGet('path')) === FALSE)
{
$message = 'Path to manual pages not configured';
return $engine->log(LOG_ERR, $message);
}
$path = explode(',', $path);
foreach($path as $p)
{
if(($dir = @opendir($p)) === FALSE)
continue;
while(($de = readdir($dir)) !== FALSE)
{
if(!is_dir($p.'/'.$de))
continue;
if(sscanf($de, 'html%s', $section) != 1)
continue;
$ret[] = $section;
}
closedir($dir);
}
natsort($ret);
return array_unique($ret);
}
//useful
//actions
//ManualModule::actions
protected function actions(Engine $engine, Request $request)
{
return array();
}
//calls
//ManualModule::callDefault
protected function callDefault(Engine $engine, Request $request)
{
$title = _('Manual browser');
if($request->getID() !== FALSE
|| $request->getTitle() !== FALSE)
return $this->callDisplay($engine, $request);
return $this->callList($engine, $request);
}
//ManualModule::callDisplay
protected function callDisplay(Engine $engine, Request $request)
{
$title = _('Manual browser');
$this->parseRequest($request, $section, $page);
if($section === FALSE || $page === FALSE)
return $this->callList($engine, $request);
$form = $this->formPage($engine, $request);
if(($res = $this->pageOpen($engine, $section, $page)) === FALSE)
{
$page = new Page(array('title' => $title));
$page->append('title', array('stock' => $this->name,
'text' => $title));
$page->append($form);
$page->append('dialog', array('type' => 'error',
'text' => _('No manual page found')));
return new PageResponse($page, Response::$CODE_ENOENT);
}
$title = sprintf(_('%s: %s(%s)'), $title, $page, $section);
$page = new Page(array('title' => $title));
$page->append('title', array('stock' => $this->name,
'text' => $title));
$page->append($form);
$vbox = $page->append('vbox');
$vbox->append('htmlview', array('class' => $this->name,
'text' => $this->_pageFormat($engine, $res)));
$vbox->append('link', array('stock' => 'back',
'request' => $this->getRequest(FALSE,
array('section' => $section)),
'text' => _('Back to the section')));
$vbox->append('link', array('stock' => 'back',
'request' => $this->getRequest(),
'text' => _('Back to the homepage')));
return new PageResponse($page);
}
private function _pageFormat(Engine $engine, DOMDocument $xml)
{
$links = $xml->getElementsByTagName('a');
foreach($links as $link)
{
if(($href = $link->attributes->getNamedItem('href'))
=== NULL)
continue;
//FIXME wrong if $page contains a dot
if(sscanf($href->textContent, '../html%[^/]/%[^.].html',
$section, $page) != 2)
continue;
$args = array('section' => $section, 'page' => $page);
$request = $this->getRequest(FALSE, $args);
$link->setAttribute('href', $engine->getURL($request));
}
$body = $xml->getElementsByTagName('body');
return ($body->length == 1)
? $xml->saveXML($body->item(0)) : $xml->saveXML();
}
//ManualModule::callList
protected function callList(Engine $engine, Request $request)
{
$title = _('Manual browser');
$this->parseRequest($request, $section, $name);
$page = new Page(array('title' => $title));
$page->append('title', array('stock' => $this->name,
'text' => $title));
$form = $this->formPage($engine, $request);
$page->append($form);
if($section !== FALSE)
return $this->_listSection($engine, $request, $page,
$section);
if($name !== FALSE)
return $this->_listPages($engine, $request, $page,
$name);
return $this->_listSections($engine, $request, $page);
}
private function _listPages(Engine $engine, Request $request,
PageElement $page, $name)
{
if(($pages = $this->getPages($engine, $name)) === FALSE)
{
$error = _('Could not list pages');
$page->append('dialog', array('type' => 'error',
'text' => $error));
return new PageResponse($page,
Response::$CODE_EUNKNOWN);
}
$columns = array('title' => _('Page'),
'section' => _('Section'),
'description' => _('Title'));
$view = $page->append('treeview', array('columns' => $columns));
foreach($pages as $r)
{
$r['description'] = $r['title'];
$args = array('section' => $r['section'],
'page' => $r['page']);
$req = $this->getRequest(FALSE, $args);
$r['title'] = new PageElement('link', array(
'request' => $req,
'text' => $r['page'],
'title' => $r['title']));
$args = array('section' => $r['section']);
$req = $this->getRequest(FALSE, $args);
$r['section'] = new PageElement('link', array(
'request' => $req,
'text' => $r['section'],
'title' => $r['section']));
$view->append('row', $r);
}
return new PageResponse($page);
}
private function _listSection(Engine $engine, Request $request,
PageElement $page, $section)
{
//FIXME implement paging
if(($pages = $this->getSectionPages($engine, $section))
=== FALSE)
{
$error = _('Could not list pages');
$page->append('dialog', array('type' => 'error',
'text' => $error));
return new PageResponse($page,
Response::$CODE_EUNKNOWN);
}
$columns = array('title' => _('Page'),
'section' => _('Section'),
'description' => _('Title'));
$view = $page->append('treeview', array('columns' => $columns));
foreach($pages as $r)
{
$r['description'] = $r['title'];
$args = array('section' => $r['section'],
'page' => $r['page']);
$req = $this->getRequest(FALSE, $args);
$r['title'] = new PageElement('link', array(
'request' => $req,
'text' => $r['page'],
'title' => $r['title']));
$view->append('row', $r);
}
return new PageResponse($page);
}
private function _listSections(Engine $engine, Request $request,
PageElement $page)
{
if(($sections = $this->getSections($engine)) === FALSE)
{
$error = _('Could not list sections');
$page->append('dialog', array('type' => 'error',
'text' => $error));
return new PageResponse($page,
Response::$CODE_EUNKNOWN);
}
$columns = array('title' => _('Section'),
'description' => _('Description'));
$view = $page->append('treeview', array('columns' => $columns));
foreach($sections as $s)
{
$r = $this->getRequest(FALSE, array(
'section' => $s));
$link = new PageElement('link', array(
'request' => $r,
'text' => $s));
$description = $this->getSectionDescription($s);
$view->append('row', array('title' => $link,
'description' => $description));
}
return new PageResponse($page);
}
//forms
//Manual::formPage
protected function formPage(Engine $engine, Request $request)
{
$r = $this->getRequest();
$this->parseRequest($request, $section, $page);
$form = new PageElement('form', array('request' => $r,
'idempotent' => TRUE));
$box = $form->append('hbox');
$combobox = $box->append('combobox', array(
'text' => _('Section: '),
'name' => 'section', 'value' => $section));
$combobox->append('label', array('value' => '',
'text' => _('Any')));
if(($sections = $this->getSections($engine)) !== FALSE)
foreach($sections as $s)
$combobox->append('label', array('value' => $s,
'text' => $s));
$box->append('entry', array('text' => _('Page: '),
'name' => 'page', 'value' => $page));
$box->append('button', array('type' => 'submit',
'text' => _('Search')));
return $form;
}
//useful
//ManualModule::pageOpen
protected function pageOpen(Engine $engine, $section, $name)
{
if(($path = $this->configGet('path')) === FALSE)
{
$message = 'Path to manual pages not configured';
return $engine->log(LOG_ERR, $message);
}
if(strpos($section, '/') !== FALSE
|| strpos($name, '/') !== FALSE)
return FALSE;
$path = explode(',', $path);
$xml = new DOMDocument();
foreach($path as $p)
{
$filename = $p.'/html'.$section.'/'.$name.'.html';
//we can ignore errors
if(@$xml->loadHTMLfile($filename, LIBXML_NOENT)
=== TRUE)
return $xml;
}
return FALSE;
}
//ManualModule::parseRequest
protected function parseRequest(Request $request, &$section = FALSE,
&$page = FALSE)
{
$section = $request->getID() ?: $request->get('section');
if(strlen($section) == 0)
$section = FALSE;
$page = $request->getTitle() ?: $request->get('page');
if(strlen($page) == 0)
$page = FALSE;
}
}
?>