3 * wallabag, self hostable application allowing you to not miss any content anymore
6 * @author Nicolas LÅ“uillet <nicolas@loeuillet.org>
8 * @license http://opensource.org/licenses/MIT see COPYING file
42 public function __construct()
47 private function init()
51 $pocheUser = Session
::getParam('poche_user');
53 if ($pocheUser && $pocheUser != array()) {
54 $this->user
= $pocheUser;
56 // fake user, just for install & login screens
57 $this->user
= new User();
58 $this->user
->setConfig($this->getDefaultConfig());
61 $this->pagination
= new Paginator($this->user
->getConfigValue('pager'), 'p');
62 $this->language
= new Language($this);
63 $this->tpl
= new Template($this);
64 $this->store
= new Database();
65 $this->messages
= new Messages();
66 $this->routing
= new Routing($this);
71 $this->routing
->run();
77 public function createNewUser()
79 if (isset($_GET['newuser'])){
80 if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
81 $newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING
);
82 if (!$this->store
->userExists($newusername)){
83 if ($this->store
->install($newusername, Tools
::encodeString($_POST['password4newuser'] . $newusername))) {
84 Tools
::logm('The new user '.$newusername.' has been installed');
85 $this->messages
->add('s', sprintf(_('The new user %s has been installed. Do you want to <a href="?logout">logout ?</a>'),$newusername));
89 Tools
::logm('error during adding new user');
94 $this->messages
->add('e', sprintf(_('Error : An user with the name %s already exists !'),$newusername));
95 Tools
::logm('An user with the name '.$newusername.' already exists !');
103 * Delete an existing user
105 public function deleteUser()
107 if (isset($_GET['deluser'])){
108 if ($this->store
->listUsers() > 1) {
109 if (Tools
::encodeString($_POST['password4deletinguser'].$this->user
->getUsername()) == $this->store
->getUserPassword($this->user
->getId())) {
110 $username = $this->user
->getUsername();
111 $this->store
->deleteUserConfig($this->user
->getId());
112 Tools
::logm('The configuration for user '. $username .' has been deleted !');
113 $this->store
->deleteTagsEntriesAndEntries($this->user
->getId());
114 Tools
::logm('The entries for user '. $username .' has been deleted !');
115 $this->store
->deleteUser($this->user
->getId());
116 Tools
::logm('User '. $username .' has been completely deleted !');
118 Tools
::logm('logout');
120 $this->messages
->add('s', sprintf(_('User %s has been successfully deleted !'),$newusername));
123 Tools
::logm('Bad password !');
124 $this->messages
->add('e', _('Error : The password is wrong !'));
128 Tools
::logm('Only user !');
129 $this->messages
->add('e', _('Error : You are the only user, you cannot delete your account !'));
134 public function getDefaultConfig()
137 'pager' => PAGINATION
,
139 'theme' => DEFAULT_THEME
144 * Call action (mark as fav, archive, delete, etc.)
146 public function action($action, Url
$url, $id = 0, $import = FALSE, $autoclose = FALSE, $tags = null)
151 $content = Tools
::getPageContent($url);
152 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
153 $body = $content['rss']['channel']['item']['description'];
155 // clean content from prevent xss attack
156 $purifier = $this->getPurifier();
157 $title = $purifier->purify($title);
158 $body = $purifier->purify($body);
160 //search for possible duplicate
162 $duplicate = $this->store
->retrieveOneByURL($url->getUrl(), $this->user
->getId());
164 $last_id = $this->store
->add($url->getUrl(), $title, $body, $this->user
->getId());
166 Tools
::logm('add link ' . $url->getUrl());
167 if (DOWNLOAD_PICTURES
) {
168 $content = Picture
::filterPicture($body, $url->getUrl(), $last_id);
169 Tools
::logm('updating content article');
170 $this->store
->updateContent($last_id, $content, $this->user
->getId());
173 if ($duplicate != NULL) {
174 // duplicate exists, so, older entry needs to be deleted (as new entry should go to the top of list), BUT favorite mark and tags should be preserved
175 Tools
::logm('link ' . $url->getUrl() . ' is a duplicate');
176 // 1) - preserve tags and favorite, then drop old entry
177 $this->store
->reassignTags($duplicate['id'], $last_id);
178 if ($duplicate['is_fav']) {
179 $this->store
->favoriteById($last_id, $this->user
->getId());
181 if ($this->store
->deleteById($duplicate['id'], $this->user
->getId())) {
182 Tools
::logm('previous link ' . $url->getUrl() .' entry deleted');
186 $this->messages
->add('s', _('the link has been added successfully'));
189 $this->messages
->add('e', _('error during insertion : the link wasn\'t added'));
190 Tools
::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
193 if ($autoclose == TRUE) {
194 Tools
::redirect('?view=home');
196 Tools
::redirect('?view=home&closewin=true');
200 $msg = 'delete link #' . $id;
201 if ($this->store
->deleteById($id, $this->user
->getId())) {
202 if (DOWNLOAD_PICTURES
) {
203 Picture
::removeDirectory(ABS_PATH
. $id);
205 $this->messages
->add('s', _('the link has been deleted successfully'));
208 $this->messages
->add('e', _('the link wasn\'t deleted'));
209 $msg = 'error : can\'t delete link #' . $id;
212 Tools
::redirect('?');
215 $this->store
->favoriteById($id, $this->user
->getId());
216 Tools
::logm('mark as favorite link #' . $id);
217 if ( Tools
::isAjaxRequest() ) {
225 case 'toggle_archive' :
226 $this->store
->archiveById($id, $this->user
->getId());
227 Tools
::logm('archive link #' . $id);
228 if ( Tools
::isAjaxRequest() ) {
237 $this->store
->archiveAll($this->user
->getId());
238 Tools
::logm('archive all links');
242 if (isset($_GET['search'])) {
243 //when we want to apply a tag to a search
244 $tags = array($_GET['search']);
245 $allentry_ids = $this->store
->search($tags[0], $this->user
->getId());
246 $entry_ids = array();
247 foreach ($allentry_ids as $eachentry) {
248 $entry_ids[] = $eachentry[0];
250 } else { //add a tag to a single article
251 $tags = explode(',', $_POST['value']);
252 $entry_ids = array($_POST['entry_id']);
254 foreach($entry_ids as $entry_id) {
255 $entry = $this->store
->retrieveOneById($entry_id, $this->user
->getId());
257 $this->messages
->add('e', _('Article not found!'));
258 Tools
::logm('error : article not found');
261 //get all already set tags to preven duplicates
262 $already_set_tags = array();
263 $entry_tags = $this->store
->retrieveTagsByEntry($entry_id);
264 foreach ($entry_tags as $tag) {
265 $already_set_tags[] = $tag['value'];
267 foreach($tags as $key => $tag_value) {
268 $value = trim($tag_value);
269 if ($value && !in_array($value, $already_set_tags)) {
270 $tag = $this->store
->retrieveTagByValue($value);
273 $tag = $this->store
->createTag($value);
275 if (STORAGE
== 'postgres') {
276 $sequence = 'tags_id_seq';
278 $tag_id = $this->store
->getLastId($sequence);
281 $tag_id = $tag['id'];
284 # we assign the tag to the article
285 $this->store
->setTagToEntry($tag_id, $entry_id);
289 $this->messages
->add('s', _('The tag has been applied successfully'));
290 Tools
::logm('The tag has been applied successfully');
294 $tag_id = $_GET['tag_id'];
295 $entry = $this->store
->retrieveOneById($id, $this->user
->getId());
297 $this->messages
->add('e', _('Article not found!'));
298 Tools
::logm('error : article not found');
301 $this->store
->removeTagForEntry($id, $tag_id);
302 Tools
::logm('tag entry deleted');
303 if ($this->store
->cleanUnusedTag($tag_id)) {
304 Tools
::logm('tag deleted');
306 $this->messages
->add('s', _('The tag has been successfully deleted'));
314 function displayView($view, $id = 0)
321 $dev_infos = $this->getPocheVersion('dev');
322 $dev = trim($dev_infos[0]);
323 $check_time_dev = date('d-M-Y H:i', $dev_infos[1]);
324 $prod_infos = $this->getPocheVersion('prod');
325 $prod = trim($prod_infos[0]);
326 $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
327 $compare_dev = version_compare(POCHE
, $dev);
328 $compare_prod = version_compare(POCHE
, $prod);
329 $themes = $this->tpl
->getInstalledThemes();
330 $languages = $this->language
->getInstalledLanguages();
331 $token = $this->user
->getConfigValue('token');
332 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
333 $only_user = ($this->store
->listUsers() > 1) ? false : true;
336 'languages' => $languages,
339 'check_time_dev' => $check_time_dev,
340 'check_time_prod' => $check_time_prod,
341 'compare_dev' => $compare_dev,
342 'compare_prod' => $compare_prod,
344 'user_id' => $this->user
->getId(),
345 'http_auth' => $http_auth,
346 'only_user' => $only_user
348 Tools
::logm('config view');
352 $entry = $this->store
->retrieveOneById($id, $this->user
->getId());
354 $this->messages
->add('e', _('Article not found!'));
355 Tools
::logm('error : article not found');
358 $tags = $this->store
->retrieveTagsByEntry($id);
366 $token = $this->user
->getConfigValue('token');
367 //if term is set - search tags for this term
368 $term = Tools
::checkVar('term');
369 $tags = $this->store
->retrieveAllTags($this->user
->getId(), $term);
370 if (Tools
::isAjaxRequest()) {
372 foreach ($tags as $tag) {
373 $result[] = $tag['value'];
375 echo json_encode($result);
380 'user_id' => $this->user
->getId(),
385 if (isset($_GET['search'])) {
386 $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING
);
387 $tpl_vars['entries'] = $this->store
->search($search, $this->user
->getId());
388 $count = count($tpl_vars['entries']);
389 $this->pagination
->set_total($count);
390 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
391 $this->pagination
->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' ));
392 $tpl_vars['page_links'] = $page_links;
393 $tpl_vars['nb_results'] = $count;
394 $tpl_vars['search_term'] = $search;
398 $entry = $this->store
->retrieveOneById($id, $this->user
->getId());
399 if ($entry != NULL) {
400 Tools
::logm('view link #' . $id);
401 $content = $entry['content'];
402 if (function_exists('tidy_parse_string')) {
403 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
404 $tidy->cleanRepair();
405 $content = $tidy->value
;
409 $flattr = new FlattrItem();
410 $flattr->checkItem($entry['url'], $entry['id']);
413 $tags = $this->store
->retrieveTagsByEntry($entry['id']);
417 'content' => $content,
423 Tools
::logm('error in view call : entry is null');
426 default: # home, favorites, archive and tag views
431 'listmode' => (isset($_COOKIE['listmode']) ? true : false),
434 //if id is given - we retrieve entries by tag: id is tag id
436 $tpl_vars['tag'] = $this->store
->retrieveTag($id, $this->user
->getId());
437 $tpl_vars['id'] = intval($id);
440 $count = $this->store
->getEntriesByViewCount($view, $this->user
->getId(), $id);
443 $this->pagination
->set_total($count);
444 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
445 $this->pagination
->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . (($id)?'&id='.$id:'') . '&' ));
446 $tpl_vars['entries'] = $this->store
->getEntriesByView($view, $this->user
->getId(), $this->pagination
->get_limit(), $id);
447 $tpl_vars['page_links'] = $page_links;
448 $tpl_vars['nb_results'] = $count;
450 Tools
::logm('display ' . $view . ' view');
458 * update the password of the current user.
459 * if MODE_DEMO is TRUE, the password can't be updated.
460 * @todo add the return value
461 * @todo set the new password in function header like this updatePassword($newPassword)
464 public function updatePassword()
467 $this->messages
->add('i', _('in demo mode, you can\'t update your password'));
468 Tools
::logm('in demo mode, you can\'t do this');
469 Tools
::redirect('?view=config');
472 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
473 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
474 $this->messages
->add('s', _('your password has been updated'));
475 $this->store
->updatePassword($this->user
->getId(), Tools
::encodeString($_POST['password'] . $this->user
->getUsername()));
477 Tools
::logm('password updated');
481 $this->messages
->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
482 Tools
::redirect('?view=config');
489 * get credentials from differents sources
490 * it redirects the user to the $referer link
493 private function credentials() {
494 if(isset($_SERVER['PHP_AUTH_USER'])) {
495 return array($_SERVER['PHP_AUTH_USER'],'php_auth',true);
497 if(!empty($_POST['login']) && !empty($_POST['password'])) {
498 return array($_POST['login'],$_POST['password'],false);
500 if(isset($_SERVER['REMOTE_USER'])) {
501 return array($_SERVER['REMOTE_USER'],'http_auth',true);
504 return array(false,false,false);
508 * checks if login & password are correct and save the user in session.
509 * it redirects the user to the $referer link
510 * @param string $referer the url to redirect after login
511 * @todo add the return value
514 public function login($referer)
516 list($login,$password,$isauthenticated)=$this->credentials();
517 if($login === false || $password === false) {
518 $this->messages
->add('e', _('login failed: you have to fill all fields'));
519 Tools
::logm('login failed');
522 if (!empty($login) && !empty($password)) {
523 $user = $this->store
->login($login, Tools
::encodeString($password . $login), $isauthenticated);
524 if ($user != array()) {
525 # Save login into Session
526 $longlastingsession = isset($_POST['longlastingsession']);
527 $passwordTest = ($isauthenticated) ? $user['password'] : Tools
::encodeString($password . $login);
528 Session
::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user)));
529 $this->messages
->add('s', _('welcome to your wallabag'));
530 Tools
::logm('login successful');
531 Tools
::redirect($referer);
533 $this->messages
->add('e', _('login failed: bad login or password'));
534 Tools
::logm('login failed');
540 * log out the poche user. It cleans the session.
541 * @todo add the return value
544 public function logout()
546 $this->user
= array();
548 Tools
::logm('logout');
553 * import datas into your poche
556 public function import() {
558 if ( isset($_FILES['file']) ) {
559 Tools
::logm('Import stated: parsing file');
561 // assume, that file is in json format
562 $str_data = file_get_contents($_FILES['file']['tmp_name']);
563 $data = json_decode($str_data, true);
565 if ( $data === null ) {
566 //not json - assume html
567 $html = new simple_html_dom();
568 $html->load_file($_FILES['file']['tmp_name']);
571 foreach (array('ol','ul') as $list) {
572 foreach ($html->find($list) as $ul) {
573 foreach ($ul->find('li') as $li) {
576 $tmpEntry['url'] = $a[0]->href
;
577 $tmpEntry['tags'] = $a[0]->tags
;
578 $tmpEntry['is_read'] = $read;
579 if ($tmpEntry['url']) {
583 # the second <ol/ul> is for read links
584 $read = ((sizeof($data) && $read)?0:1);
589 //for readability structure
590 foreach ($data as $record) {
591 if (is_array($record)) {
593 foreach ($record as $record2) {
594 if (is_array($record2)) {
601 $urlsInserted = array(); //urls of articles inserted
602 foreach ($data as $record) {
603 $url = trim( isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '') );
604 if ( $url and !in_array($url, $urlsInserted) ) {
605 $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ').'</a> <a href="./?import">'._('click to finish import').'</a><a>');
606 $body = (isset($record['content']) ? $record['content'] : '');
607 $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive'])?intval($record['archive']):0));
608 $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite'])?intval($record['favorite']):0) );
610 $id = $this->store
->add($url, $title, $body, $this->user
->getId(), $isFavorite, $isRead);
612 $urlsInserted[] = $url; //add
614 if ( isset($record['tags']) && trim($record['tags']) ) {
622 $i = sizeof($urlsInserted);
624 $this->messages
->add('s', _('Articles inserted: ').$i._('. Please note, that some may be marked as "read".'));
626 Tools
::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).');
628 //file parsing finished here
630 //now download article contents if any
632 //check if we need to download any content
633 $recordsDownloadRequired = $this->store
->retrieveUnfetchedEntriesCount($this->user
->getId());
634 if ( $recordsDownloadRequired == 0 ) {
635 //nothing to download
636 $this->messages
->add('s', _('Import finished.'));
637 Tools
::logm('Import finished completely');
641 //if just inserted - don't download anything, download will start in next reload
642 if ( !isset($_FILES['file']) ) {
643 //download next batch
644 Tools
::logm('Fetching next batch of articles...');
645 $items = $this->store
->retrieveUnfetchedEntries($this->user
->getId(), IMPORT_LIMIT
);
647 $purifier = $this->getPurifier();
649 foreach ($items as $item) {
650 $url = new Url(base64_encode($item['url']));
651 Tools
::logm('Fetching article '.$item['id']);
652 $content = Tools
::getPageContent($url);
654 $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'));
655 $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined'));
657 //clean content to prevent xss attack
658 $title = $purifier->purify($title);
659 $body = $purifier->purify($body);
661 $this->store
->updateContentAndTitle($item['id'], $title, $body, $this->user
->getId());
662 Tools
::logm('Article '.$item['id'].' updated.');
668 return array('includeImport'=>true, 'import'=>array('recordsDownloadRequired'=>$recordsDownloadRequired, 'recordsUnderDownload'=> IMPORT_LIMIT
, 'delay'=> IMPORT_DELAY
* 1000) );
672 * export poche entries in json
673 * @return json all poche entries
675 public function export() {
676 $filename = "wallabag-export-".$this->user
->getId()."-".date("Y-m-d").".json";
677 header('Content-Disposition: attachment; filename='.$filename);
679 $entries = $this->store
->retrieveAll($this->user
->getId());
680 echo $this->tpl
->render('export.twig', array(
681 'export' => Tools
::renderJson($entries),
683 Tools
::logm('export view');
687 * Checks online the latest version of poche and cache it
688 * @param string $which 'prod' or 'dev'
689 * @return string latest $which version
691 private function getPocheVersion($which = 'prod') {
692 $cache_file = CACHE
. '/' . $which;
693 $check_time = time();
695 # checks if the cached version file exists
696 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
697 $version = file_get_contents($cache_file);
698 $check_time = filemtime($cache_file);
700 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
701 file_put_contents($cache_file, $version, LOCK_EX
);
703 return array($version, $check_time);
706 public function generateToken()
708 if (ini_get('open_basedir') === '') {
709 if (strtoupper(substr(PHP_OS
, 0, 3)) === 'WIN') {
710 echo 'This is a server using Windows!';
711 // alternative to /dev/urandom for Windows
712 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
714 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
718 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
721 $token = str_replace('+', '', $token);
722 $this->store
->updateUserConfig($this->user
->getId(), 'token', $token);
723 $currentConfig = $_SESSION['poche_user']->config
;
724 $currentConfig['token'] = $token;
725 $_SESSION['poche_user']->setConfig($currentConfig);
729 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
731 $allowed_types = array('home', 'fav', 'archive', 'tag');
732 $config = $this->store
->getConfigUser($user_id);
734 if ($config == null) {
735 die(sprintf(_('User with this id (%d) does not exist.'), $user_id));
738 if (!in_array($type, $allowed_types) || $token != $config['token']) {
739 die(_('Uh, there is a problem while generating feeds.'));
743 $feed = new FeedWriter(RSS2
);
744 $feed->setTitle('wallabag — ' . $type . ' feed');
745 $feed->setLink(Tools
::getPocheUrl());
746 $feed->setChannelElement('pubDate', date(DATE_RSS
, time()));
747 $feed->setChannelElement('generator', 'wallabag');
748 $feed->setDescription('wallabag ' . $type . ' elements');
750 if ($type == 'tag') {
751 $entries = $this->store
->retrieveEntriesByTag($tag_id, $user_id);
754 $entries = $this->store
->getEntriesByView($type, $user_id);
757 if (count($entries) > 0) {
758 foreach ($entries as $entry) {
759 $newItem = $feed->createNewItem();
760 $newItem->setTitle($entry['title']);
761 $newItem->setSource(Tools
::getPocheUrl() . '?view=view&id=' . $entry['id']);
762 $newItem->setLink($entry['url']);
763 $newItem->setDate(time());
764 $newItem->setDescription($entry['content']);
765 $feed->addItem($newItem);
769 $feed->genarateFeed();
773 public function emptyCache() {
774 $files = new RecursiveIteratorIterator(
775 new RecursiveDirectoryIterator(CACHE
, RecursiveDirectoryIterator
::SKIP_DOTS
),
776 RecursiveIteratorIterator
::CHILD_FIRST
779 foreach ($files as $fileinfo) {
780 $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
781 $todo($fileinfo->getRealPath());
784 Tools
::logm('empty cache');
785 $this->messages
->add('s', _('Cache deleted.'));
790 * return new purifier object with actual config
792 protected function getPurifier() {
793 $config = HTMLPurifier_Config
::createDefault();
794 $config->set('Cache.SerializerPath', CACHE
);
795 $config->set('HTML.SafeIframe', true);
797 //allow YouTube, Vimeo and dailymotion videos
798 $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/|www\.dailymotion\.com/embed/video/)%');
800 return new HTMLPurifier($config);
806 public function createEpub() {
808 switch ($_GET['method']) {
810 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT
);
811 $entry = $this->store
->retrieveOneById($entryID, $this->user
->getId());
812 $entries = array($entry);
813 $bookTitle = $entry['title'];
814 $bookFileName = substr($bookTitle, 0, 200);
817 $entries = $this->store
->retrieveAll($this->user
->getId());
818 $bookTitle = sprintf(_('All my articles on '), date(_('d.m.y'))); #translatable because each country has it's own date format system
819 $bookFileName = _('Allarticles') . date(_('dmY'));
822 $tag = filter_var($_GET['tag'],FILTER_SANITIZE_STRING
);
823 $tags_id = $this->store
->retrieveAllTags($this->user
->getId(),$tag);
824 $tag_id = $tags_id[0]["id"]; // we take the first result, which is supposed to match perfectly. There must be a workaround.
825 $entries = $this->store
->retrieveEntriesByTag($tag_id,$this->user
->getId());
826 $bookTitle = sprintf(_('Articles tagged %s'),$tag);
827 $bookFileName = substr(sprintf(_('Tag %s'),$tag), 0, 200);
830 $category = filter_var($_GET['category'],FILTER_SANITIZE_STRING
);
831 $entries = $this->store
->getEntriesByView($category,$this->user
->getId());
832 $bookTitle = sprintf(_('All articles in category %s'), $category);
833 $bookFileName = substr(sprintf(_('Category %s'),$category), 0, 200);
836 $search = filter_var($_GET['search'],FILTER_SANITIZE_STRING
);
837 $entries = $this->store
->search($search,$this->user
->getId());
838 $bookTitle = sprintf(_('All articles for search %s'), $search);
839 $bookFileName = substr(sprintf(_('Search %s'), $search), 0, 200);
842 die(_('Uh, there is a problem while generating epub.'));
847 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
848 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
850 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
851 . "<title>wallabag articles book</title>\n"
855 $bookEnd = "</body>\n</html>\n";
857 $log = new Logger("wallabag", TRUE);
860 $book = new EPub(EPub
::BOOK_VERSION_EPUB3
, DEBUG_POCHE
);
861 $log->logLine("new EPub()");
862 $log->logLine("EPub class version: " . EPub
::VERSION
);
863 $log->logLine("EPub Req. Zip version: " . EPub
::REQ_ZIP_VERSION
);
864 $log->logLine("Zip version: " . Zip
::VERSION
);
865 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
866 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
868 $book->setTitle(_('wallabag\'s articles'));
869 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub
::IDENTIFIER_URI
); // Could also be the ISBN number, prefered for published books, or a UUID.
870 //$book->setLanguage("en"); // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc.
871 $book->setDescription(_("Some articles saved on my wallabag"));
872 $book->setAuthor("wallabag","wallabag");
873 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
874 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
875 //$book->setRights("Copyright and licence information specific for the book."); // As this is generated, this _could_ contain the name or licence information of the user who purchased the book, if needed. If this is used that way, the identifier must also be made unique for the book.
876 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
878 $book->addDublinCoreMetadata(DublinCore
::CONTRIBUTOR
, "PHP");
879 $book->addDublinCoreMetadata(DublinCore
::CONTRIBUTOR
, "wallabag");
881 $cssData = "body {\n margin-left: .5em;\n margin-right: .5em;\n text-align: justify;\n}\n\np {\n font-family: serif;\n font-size: 10pt;\n text-align: justify;\n text-indent: 1em;\n margin-top: 0px;\n margin-bottom: 1ex;\n}\n\nh1, h2 {\n font-family: sans-serif;\n font-style: italic;\n text-align: center;\n background-color: #6b879c;\n color: white;\n width: 100%;\n}\n\nh1 {\n margin-bottom: 2px;\n}\n\nh2 {\n margin-top: -2px;\n margin-bottom: 2px;\n}\n";
883 $log->logLine("Add Cover");
885 $fullTitle = "<h1> " . $bookTitle . "</h1>\n";
887 $book->setCoverImage("Cover.png", file_get_contents("themes/baggy/img/apple-touch-icon-152.png"), "image/png", $fullTitle);
889 $cover = $content_start . '<div style="text-align:center;"><p>' . _('Produced by wallabag with PHPePub') . '</p><p>'. _('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.') . '</p></div>' . $bookEnd;
891 //$book->addChapter("Table of Contents", "TOC.xhtml", NULL, false, EPub::EXTERNAL_REF_IGNORE);
892 $book->addChapter("Notices", "Cover2.html", $cover);
896 foreach ($entries as $entry) { //set tags as subjects
897 $tags = $this->store
->retrieveTagsByEntry($entry['id']);
898 foreach ($tags as $tag) {
899 $book->setSubject($tag['value']);
902 $log->logLine("Set up parameters");
904 $chapter = $content_start . $entry['content'] . $bookEnd;
905 $book->addChapter($entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub
::EXTERNAL_REF_ADD
);
906 $log->logLine("Added chapter " . $entry['title']);
910 $epuplog = $book->getLog();
911 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // log generation
914 $zipData = $book->sendBook($bookFileName);