]> _ Git - cubeextranet.git/commitdiff
wait #2645 @0.5
authorvincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 21 Mar 2019 18:17:50 +0000 (18:17 +0000)
committervincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Thu, 21 Mar 2019 18:17:50 +0000 (18:17 +0000)
inc/ws/Controlleur/class.ws.services.php

index e8b711252293eb18d6f833d2332af7809ea60c63..bcbc75d7cf52ad944b51a8a9a48f6aa4ad5e1d66 100644 (file)
 class wsServices extends cubeFlashGateway
 {
 
-       const CNAME = __CLASS__;
-
-       public static function in($args)
-       {
-               global $core;
-               $args = cubePage::getArgs($args);
-               $n = self::CNAME;
-               $gateway = new $n($core->con, $args);
-       }
-
-       public function sendEmail()
-       {
-               global $core;
-               if (!$this->_checkHash()) {
-                       return;
-               }
-
-               $dao = new wsDAOBook($core->con);
-               $book = $dao->selectById($this->args['id']);
-
-               $return = 'postmaster@fluidbook.com';
-
-               // Send the email
-               $mail = new cubeMail();
-               $mail->returnPath = $return;
-               $mail->acknowledge = isset($this->args['askAcknowledge']) && $this->args['askAcknowledge'];
-               $mail->charset = 'UTF-8';
-               $mail->to = $this->args['email'];
-               if ($book->parametres->sendasfluidbook) {
-                       $mail->from = $this->args['fromname'] . '<' . $return . '>';
-                       $mail->replyTo = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
-               } else {
-                       $mail->from = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
-               }
-               $mail->subject = $this->args['subject'];
-               $mail->body = $this->args['body'];
-               $this->xml->addChild('ok', $mail->send() ? '1' : '0');
-       }
-
-       protected function _checkHash()
-       {
-               // Check protection hash
-               $hash = md5(substr($this->args['fromemail'], 2, 6) . substr($this->args['email'], 3, 5) . 'SFGHF566!S' . $this->args['id']);
-               if ($hash != $this->args['hash']) {
-                       $this->xml->addChild('hashOK', '0');
-                       $this->xml->addChild('ok', '0');
-                       return false;
-               }
-               $this->xml->addChild('hashOK', '1');
-               return true;
-       }
-
-       public function sendBookmarks()
-       {
-               if (!$this->_checkHash()) {
-                       return;
-               }
-
-               global $core;
-               $dao = new wsDAOBook($core->con);
-               $book = $dao->selectById($this->args['id']);
-
-               $bookmarks = json_decode(base64_decode($this->args['bookmarks']), true);
-               $files = array();
-               foreach ($bookmarks as $b) {
-                       $range = $b['page'];
-                       if ($b['nb'] > 1) {
-                               $range .= '-' . ($b['page'] + $b['nb'] - 1);
-                       }
-
-                       $file = $this->getPDFComplex($this->args['id'], $range);
-                       $files[] = array('name' => $b['name'], 'file' => $file['file']);
-               }
-
-               $limit = 5 * 1024 * 1024 * 0.8;
-               $groups = array();
-               $group = array();
-               $groupsize = 0;
-               foreach ($files as $f) {
-                       $size = filesize($f['file']);
-
-                       if ($groupsize + $size > $limit) {
-                               if (count($group)) {
-                                       $groups[] = $group;
-                                       $group = array();
-                                       $groupsize = 0;
-                               }
-
-                               $group[] = $f;
-                               $groupsize = $size;
-
-                               if ($groupsize > $limit) {
-                                       $groups[] = $group;
-                                       $group = array();
-                                       $groupsize = 0;
-                               }
-                       } else {
-                               $group[] = $f;
-                               $groupsize += $size;
-                       }
-               }
-
-               if (count($group)) {
-                       $groups[] = $group;
-               }
-
-               $return = 'postmaster@fluidbook.com';
-
-               $total = count($groups);
-               foreach ($groups as $i => $g) {
-                       $s = '';
-                       if ($total > 1) {
-                               $s = ' (' . ($i + 1) . '/' . $total . ')';
-                       }
-
-                       // Send the email
-                       $mail = new cubeMail();
-                       $mail->returnPath = $return;
-                       $mail->acknowledge = isset($this->args['askAcknowledge']) && $this->args['askAcknowledge'];
-                       $mail->charset = 'UTF-8';
-                       $mail->to = $this->args['email'];
-                       if ($book->parametres->sendasfluidbook) {
-                               $mail->from = $this->args['fromname'] . '<' . $return . '>';
-                               $mail->replyTo = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
-                       } else {
-                               $mail->from = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
-                       }
-                       $mail->subject = $this->args['subject'] . $s;
-                       $mail->body = $this->args['body'];
-                       foreach ($g as $f) {
-                               $mail->addFile($f['name'] . '.pdf', $f['file']);
-                       }
-                       $this->xml->addChild('ok', $mail->send() ? '1' : '0');
-               }
-       }
-
-       protected function shortenURL($url, $id)
-       {
-               $bitLyUser = 'fluidbook';
-               $bitLyKey = 'R_3858dd1c9884d5c6a5fe386d7e95cf1d';
-               // Recherche dans le cache
-               $r = $this->con->select('SELECT * FROM book_short_url WHERE long_url=\'' . $this->con->escape($url) . '\' LIMIT 1');
-               if ($r->count() > 0) {
-                       return $r->short_url;
-               }
-               // Si pas dans le cache, on le recherche
-               $short_url = cubeURLShortener::bitLy($url, $bitLyUser, $bitLyKey);
-               if (is_null($short_url) || empty($short_url) || !$short_url) {
-                       $short_url = cubeURLShortener::tinyURL($url);
-               }
-               $short_url = trim($short_url);
-
-               $c = $this->con->openCursor('book_short_url');
-               $c->long_url = $url;
-               $c->book_id = $id;
-               $c->short_url = $short_url;
-               $c->insert();
-
-               return $short_url;
-       }
-
-       /**
-        * wsServices::facebook_thumbnail()
-        *
-        * @return
-        */
-       public function facebook_thumbnail()
-       {
-               $this->outputXML = false;
-               $dao = new wsDAOBook($this->con);
-
-               $book = $dao->selectById($this->args['id']);
-
-               if (isset($this->args['image'])) {
-                       $cover = WS_BOOKS . '/working/' . $this->args['id'] . '/' . $this->args['image'];
-               } else {
-
-                       if (isset($book->parametres->facebook_image) && $book->parametres->facebook_image != '') {
-                               $c = WS_BOOKS . '/working/' . $this->args['id'] . '/' . $book->parametres->facebook_image;
-                               if (file_exists($c)) {
-                                       $cover = $c;
-                               }
-                       } else if ($book->parametres->pdfThumbnails) {
-                               $pdf = wsDAOBook::getWorkingFile($book->parametres->pdfThumbnails, $book->book_id);
-
-                               if (file_exists($pdf)) {
-                                       $dir = WS_CACHE . '/thumbs/' . sha1($pdf) . '/';
-                                       if (!file_exists($dir)) {
-                                               mkdir($dir, 0777, true);
-                                       }
-                                       $jpeg = $dir . '/cover.jpg';
-                                       $mtime = filemtime($jpeg);
-
-                                       if (!file_exists($jpeg) || $mtime < filemtime(__FILE__) || $mtime < filemtime($pdf)) {
-                                               wsPDFConvert::makeShot($pdf, $jpeg, 1, '', 150, 90, 4, 'PNM', 1200, -1);
-                                       }
-                                       $cover = $jpeg;
-                               }
-                       }
-
-                       if (!isset($cover)) {
-                               $pages = $dao->getPagesOfBook($this->args['id']);
-                               $dir = wsDocument::getDir($pages[1]['document_id']);
-                               $cover = $dir . '/html/t150-' . $pages[1]['document_page'] . '.jpg';
-                               if (!file_exists($cover)) {
-                                       $cover = $dir . '/p' . $pages[1]['document_page'] . '.jpg';
-                               }
-                               $i = WS_FILES . '/cover/' . $this->args['id'] . '.jpg';
-
-                               if (!file_exists($i) || filemtime(__FILE__) > filemtime($i) || filemtime($cover) > filemtime($i)) {
-                                       $it = new imageTools();
-                                       $it->loadImage($cover);
-
-                                       $it->resize(1200, 1000000, 'ratio', false);
-                                       $it->output('jpeg', $i, 85);
-                                       $it->close();
-                               }
-                               $cover = $i;
-                       }
-               }
-
-
-               cubeHTTP::relayFile($cover);
-               exit;
-       }
-
-       public function facebookShare()
-       {
-               http::redirect('http://www.facebook.com/sharer/sharer.php?u=' . urlencode($this->args['url']));
-               exit;
-       }
-
-       public function googleplusShare()
-       {
-               http::redirect('https://plus.google.com/share?url=' . urlencode($this->args['url']) . "&sgp=1");
-               exit;
-       }
-
-       public function linkedinShare()
-       {
-               http::redirect('https://www.linkedin.com/cws/share?url=' . urlencode($this->args['url']) . '&isFramed=true&_ts=' . microtime(true));
-       }
-
-       public function viadeoShare()
-       {
-               http::redirect('http://www.viadeo.com/shareit/share/?url=' . urlencode($this->args['url']));
-       }
-
-       public function twitterShare()
-       {
-               $url = $this->shortenURL($this->args['url'], $this->args['id']);
-
-               $post = str_replace('%short%', $url, $this->args['post']);
-               $post = rawurlencode($post);
-
-               http::redirect('http://twitter.com/intent/tweet?source=webclient&text=' . $post);
-               exit;
-       }
-
-       public function searchHints()
-       {
-               $index = Zend_Search_Lucene::open(WS_BOOKS . '/search/' . $this->args['id']);
-
-               $charsLimit = 2;
-
-               $terms = $this->getTerms($this->args['term']);
-               $term = array_pop($terms);
-
-               if (strlen($term) < $charsLimit) {
-                       $this->xml->addChild('hints', '{}');
-                       return;
-               }
-
-               $term = new Zend_Search_Lucene_Index_Term($term . '*', 'contents');
-               $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
-               $query->setMinPrefixLength($charsLimit);
-
-               $index->find($query);
-               $terms = $query->getQueryTerms();
-               $res = array();
-               foreach ($terms as $t) {
-                       $res[$t->text] = array_sum($index->termFreqs($t));
-               }
-               arsort($res);
-               $res = array_slice($res, 0, 10, true);
-               $this->xml->addChild('hints', json_encode($res));
-       }
-
-       public function searchResults()
-       {
-               $index = Zend_Search_Lucene::open(WS_BOOKS . '/search/' . $this->args['id']);
-
-               $terms = $this->getTerms($this->args['term']);
-
-               $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
-               foreach ($terms as $term) {
-                       $query->addTerm(new Zend_Search_Lucene_Index_Term($term, 'contents'), null);
-               }
-
-               $hits = $index->find($query);
-               $res = array();
-               $terms = array();
-               foreach ($query->getQueryTerms() as $t) {
-                       $terms[] = $t->text;
-                       foreach ($index->termFreqs($t) as $doc_id => $f) {
-                               $page = trim($index->getDocument($doc_id)->getField('url')->getUtf8Value(), "#");
-                               if (!isset($res[$page])) {
-                                       $res[$page] = 0;
-                               }
-                               $res[$page] += $f;
-                       }
-               }
-               $this->xml->addChild('results', json_encode($res));
-               $this->xml->addChild('terms', implode(',', $terms));
-       }
-
-       protected function getTerms($term)
-       {
-               $term = trim($term, '*? ');
-               $term = mb_strtolower($term);
-               $term = cubeText::removeAccents($term);
-               $terms = explode(' ', $term);
-               return $terms;
-       }
-
-       public function printpdf()
-       {
-               $this->outputXML = false;
-               $this->exportpdf(true);
-       }
-
-       public function getPDFComplex($book = null, $range = null)
-       {
-               global $core;
-
-               if (is_null($book)) {
-                       $book = $this->callArgs[0];
-                       $range = $this->callArgs[1];
-               }
-
-               $daoBook = new wsDAOBook($core->con);
-
-               if (is_int($book) || is_string($book)) {
-                       $book = $daoBook->selectById($book);
-               }
-
-               if (is_null($book)) {
-                       return false;
-               }
-
-               // Normalize range
-               $range = cubeArray::parseRange($range);
-
-               if ($k = array_search(0, $range)) {
-                       $range[$k] = 1;
-               }
-               if ($k = array_search($book->parametres->pages + 1, $range)) {
-                       $range[$k] = $r->pages;
-               }
-
-               $range = array_unique($range);
-               sort($range);
-
-
-               // Paths init
-               $baseDocument = $this->getPDFComplexBaseDocument($book);
-
-               if (!file_exists($baseDocument)) {
-                       return false;
-               }
-
-               if (!count($range)) {
-                       return false;
-               }
-
-               $destDir = WS_CACHE . '/exportpdf/' . $book->cid;
-               if (!file_exists($destDir)) {
-                       mkdir($destDir, 0777, true);
-               }
-               $fname = md5($baseDocument) . '-' . md5(implode(',%ù', $range)) . '.pdf';
-               $destFile = $destDir . '/' . $fname;
-               $destURL = '/fluidbook/cache/exportpdf/' . $book->cid . '/' . $fname;
-
-               // If result exists, don't make the pdf again
-               if (file_exists($destFile) && filemtime($destFile) > filemtime($baseDocument)) {
-
-               } else {
-                       if ($range[0] == 1 && count($range) == $book->parametres->pages && $range[$book->parametres->pages - 1] == $book->parametres->pages) {
-                               `cp $baseDocument $destFile`;
-                       } else {
-
-                               // Prepare the command line
-                               $l = array('A="' . $baseDocument . '"', 'cat');
-                               foreach ($range as $page) {
-                                       if ($page < 1 || $page > $book->parametres->pages) {
-                                               continue;
-                                       }
-                                       $l[] = 'A' . $page;
-                               }
-                               $l[] = 'output';
-                               $l[] = $destFile;
-
-                               $args = implode(' ', $l);
-                               // Execute the command line
-                               $pdftk = new cubeCommandLine('pdftk');
-                               $pdftk->setPath(CONVERTER_PATH);
-                               $pdftk->setManualArg($args);
-                               $pdftk->execute();
-                       }
-               }
-
-               return array('url' => $destURL, 'file' => $destFile);
-       }
-
-       public function getPDFComplexBaseDocument($book)
-       {
-               global $core;
-               switch ($book->parametres->bookmarkUsePDF) {
-                       case 'download':
-                               return WS_BOOKS . '/working/' . $book->book_id . '/' . $book->parametres->pdfReplace;
-                               break;
-                       case 'thumbnails':
-                               return WS_BOOKS . '/working/' . $book->book_id . '/' . $book->parametres->pdfThumbnails;
-                               break;
-                       case 'pages':
-                       default:
-                               $normal = WS_BOOKS . '/pdf/' . $book->book_id . '/original.pdf';
-                               if (!file_exists($normal)) {
-                                       $daoBook = new wsDAOBook($core->con);
-                                       $pages = $daoBook->getPagesOfBook($book->book_id);
-                                       $daoBook->compilePDF($book, $pages);
-                               }
-                               return $normal;
-                               break;
-               }
-               return $res;
-       }
-
-       public function e()
-       {
-               $this->args['cid'] = $this->callArgs[0];
-               $this->args['range'] = $this->callArgs[1];
-               return $this->exportpdf();
-       }
-
-       public function p()
-       {
-               $this->args['cid'] = $this->callArgs[0];
-               $this->args['range'] = $this->callArgs[1];
-               return $this->exportpdf(true);
-       }
-
-       public function exportpdf($print = false)
-       {
-               global $core;
-
-               set_time_limit(0);
-
-               $dao = new wsDAOBook($core->con);
-               if (isset($this->args['cid'])) {
-                       $book = $dao->selectByCid($this->args['cid']);
-               } else if ($this->args['id'] < 11202) {
-                       $book = $dao->selectById($this->args['id']);
-               }
-
-               $dest = $this->getPDFComplex($book, $this->args['range']);
-
-               if ($dest === false) {
-                       http_response_code(404);
-                       exit;
-               }
-
-               if (!$print) {
-                       // Return the url of the resulting pdf
-                       http::redirect($dest['url']);
-                       exit;
-               } else {
-                       $this->outputXML = false;
-                       $res = '<!DOCTYPE html><html><head>
+    const CNAME = __CLASS__;
+
+    public static function in($args)
+    {
+        global $core;
+        $args = cubePage::getArgs($args);
+        $n = self::CNAME;
+        $gateway = new $n($core->con, $args);
+    }
+
+    public function sendEmail()
+    {
+        global $core;
+        if (!$this->_checkHash()) {
+            return;
+        }
+
+        $dao = new wsDAOBook($core->con);
+        $book = $dao->selectById($this->args['id']);
+
+        $return = 'postmaster@fluidbook.com';
+
+        // Send the email
+        $mail = new cubeMail();
+        $mail->returnPath = $return;
+        $mail->acknowledge = isset($this->args['askAcknowledge']) && $this->args['askAcknowledge'];
+        $mail->charset = 'UTF-8';
+        $mail->to = $this->args['email'];
+        if ($book->parametres->sendasfluidbook) {
+            $mail->from = $this->args['fromname'] . '<' . $return . '>';
+            $mail->replyTo = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
+        } else {
+            $mail->from = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
+        }
+        $mail->subject = $this->args['subject'];
+        $mail->body = $this->args['body'];
+        $this->xml->addChild('ok', $mail->send() ? '1' : '0');
+    }
+
+    protected function _checkHash()
+    {
+        // Check protection hash
+        $hash = md5(substr($this->args['fromemail'], 2, 6) . substr($this->args['email'], 3, 5) . 'SFGHF566!S' . $this->args['id']);
+        if ($hash != $this->args['hash']) {
+            $this->xml->addChild('hashOK', '0');
+            $this->xml->addChild('ok', '0');
+            return false;
+        }
+        $this->xml->addChild('hashOK', '1');
+        return true;
+    }
+
+    public function sendBookmarks()
+    {
+        if (!$this->_checkHash()) {
+            return;
+        }
+
+        global $core;
+        $dao = new wsDAOBook($core->con);
+        $book = $dao->selectById($this->args['id']);
+
+        $bookmarks = json_decode(base64_decode($this->args['bookmarks']), true);
+        $files = array();
+        foreach ($bookmarks as $b) {
+            $range = $b['page'];
+            if ($b['nb'] > 1) {
+                $range .= '-' . ($b['page'] + $b['nb'] - 1);
+            }
+
+            $file = $this->getPDFComplex($this->args['id'], $range);
+            $files[] = array('name' => $b['name'], 'file' => $file['file']);
+        }
+
+        $limit = 5 * 1024 * 1024 * 0.8;
+        $groups = array();
+        $group = array();
+        $groupsize = 0;
+        foreach ($files as $f) {
+            $size = filesize($f['file']);
+
+            if ($groupsize + $size > $limit) {
+                if (count($group)) {
+                    $groups[] = $group;
+                    $group = array();
+                    $groupsize = 0;
+                }
+
+                $group[] = $f;
+                $groupsize = $size;
+
+                if ($groupsize > $limit) {
+                    $groups[] = $group;
+                    $group = array();
+                    $groupsize = 0;
+                }
+            } else {
+                $group[] = $f;
+                $groupsize += $size;
+            }
+        }
+
+        if (count($group)) {
+            $groups[] = $group;
+        }
+
+        $return = 'postmaster@fluidbook.com';
+
+        $total = count($groups);
+        foreach ($groups as $i => $g) {
+            $s = '';
+            if ($total > 1) {
+                $s = ' (' . ($i + 1) . '/' . $total . ')';
+            }
+
+            // Send the email
+            $mail = new cubeMail();
+            $mail->returnPath = $return;
+            $mail->acknowledge = isset($this->args['askAcknowledge']) && $this->args['askAcknowledge'];
+            $mail->charset = 'UTF-8';
+            $mail->to = $this->args['email'];
+            if ($book->parametres->sendasfluidbook) {
+                $mail->from = $this->args['fromname'] . '<' . $return . '>';
+                $mail->replyTo = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
+            } else {
+                $mail->from = $this->args['fromname'] . '<' . $this->args['fromemail'] . '>';
+            }
+            $mail->subject = $this->args['subject'] . $s;
+            $mail->body = $this->args['body'];
+            foreach ($g as $f) {
+                $mail->addFile($f['name'] . '.pdf', $f['file']);
+            }
+            $this->xml->addChild('ok', $mail->send() ? '1' : '0');
+        }
+    }
+
+    protected function shortenURL($url, $id)
+    {
+        $bitLyUser = 'fluidbook';
+        $bitLyKey = 'R_3858dd1c9884d5c6a5fe386d7e95cf1d';
+        // Recherche dans le cache
+        $r = $this->con->select('SELECT * FROM book_short_url WHERE long_url=\'' . $this->con->escape($url) . '\' LIMIT 1');
+        if ($r->count() > 0) {
+            return $r->short_url;
+        }
+        // Si pas dans le cache, on le recherche
+        $short_url = cubeURLShortener::bitLy($url, $bitLyUser, $bitLyKey);
+        if (is_null($short_url) || empty($short_url) || !$short_url) {
+            $short_url = cubeURLShortener::tinyURL($url);
+        }
+        $short_url = trim($short_url);
+
+        $c = $this->con->openCursor('book_short_url');
+        $c->long_url = $url;
+        $c->book_id = $id;
+        $c->short_url = $short_url;
+        $c->insert();
+
+        return $short_url;
+    }
+
+    /**
+     * wsServices::facebook_thumbnail()
+     *
+     * @return
+     */
+    public function facebook_thumbnail()
+    {
+        $this->outputXML = false;
+        $dao = new wsDAOBook($this->con);
+
+        $book = $dao->selectById($this->args['id']);
+
+        if (isset($this->args['image'])) {
+            $cover = WS_BOOKS . '/working/' . $this->args['id'] . '/' . $this->args['image'];
+        } else {
+
+            if (isset($book->parametres->facebook_image) && $book->parametres->facebook_image != '') {
+                $c = WS_BOOKS . '/working/' . $this->args['id'] . '/' . $book->parametres->facebook_image;
+                if (file_exists($c)) {
+                    $cover = $c;
+                }
+            } else if ($book->parametres->pdfThumbnails) {
+                $pdf = wsDAOBook::getWorkingFile($book->parametres->pdfThumbnails, $book->book_id);
+
+                if (file_exists($pdf)) {
+                    $dir = WS_CACHE . '/thumbs/' . sha1($pdf) . '/';
+                    if (!file_exists($dir)) {
+                        mkdir($dir, 0777, true);
+                    }
+                    $jpeg = $dir . '/cover.jpg';
+                    $mtime = filemtime($jpeg);
+
+                    if (!file_exists($jpeg) || $mtime < filemtime(__FILE__) || $mtime < filemtime($pdf)) {
+                        wsPDFConvert::makeShot($pdf, $jpeg, 1, '', 150, 90, 4, 'PNM', 1200, -1);
+                    }
+                    $cover = $jpeg;
+                }
+            }
+
+            if (!isset($cover)) {
+                $pages = $dao->getPagesOfBook($this->args['id']);
+                $dir = wsDocument::getDir($pages[1]['document_id']);
+                $cover = $dir . '/html/t150-' . $pages[1]['document_page'] . '.jpg';
+                if (!file_exists($cover)) {
+                    $cover = $dir . '/p' . $pages[1]['document_page'] . '.jpg';
+                }
+                $i = WS_FILES . '/cover/' . $this->args['id'] . '.jpg';
+
+                if (!file_exists($i) || filemtime(__FILE__) > filemtime($i) || filemtime($cover) > filemtime($i)) {
+                    $it = new imageTools();
+                    $it->loadImage($cover);
+
+                    $it->resize(1200, 1000000, 'ratio', false);
+                    $it->output('jpeg', $i, 85);
+                    $it->close();
+                }
+                $cover = $i;
+            }
+        }
+
+
+        cubeHTTP::relayFile($cover);
+        exit;
+    }
+
+    public function facebookShare()
+    {
+        http::redirect('http://www.facebook.com/sharer/sharer.php?u=' . urlencode($this->args['url']));
+        exit;
+    }
+
+    public function googleplusShare()
+    {
+        http::redirect('https://plus.google.com/share?url=' . urlencode($this->args['url']) . "&sgp=1");
+        exit;
+    }
+
+    public function linkedinShare()
+    {
+        http::redirect('https://www.linkedin.com/cws/share?url=' . urlencode($this->args['url']) . '&isFramed=true&_ts=' . microtime(true));
+    }
+
+    public function viadeoShare()
+    {
+        http::redirect('http://www.viadeo.com/shareit/share/?url=' . urlencode($this->args['url']));
+    }
+
+    public function twitterShare()
+    {
+        $url = $this->shortenURL($this->args['url'], $this->args['id']);
+
+        $post = str_replace('%short%', $url, $this->args['post']);
+        $post = rawurlencode($post);
+
+        http::redirect('http://twitter.com/intent/tweet?source=webclient&text=' . $post);
+        exit;
+    }
+
+    public function searchHints()
+    {
+        $index = Zend_Search_Lucene::open(WS_BOOKS . '/search/' . $this->args['id']);
+
+        $charsLimit = 2;
+
+        $terms = $this->getTerms($this->args['term']);
+        $term = array_pop($terms);
+
+        if (strlen($term) < $charsLimit) {
+            $this->xml->addChild('hints', '{}');
+            return;
+        }
+
+        $term = new Zend_Search_Lucene_Index_Term($term . '*', 'contents');
+        $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
+        $query->setMinPrefixLength($charsLimit);
+
+        $index->find($query);
+        $terms = $query->getQueryTerms();
+        $res = array();
+        foreach ($terms as $t) {
+            $res[$t->text] = array_sum($index->termFreqs($t));
+        }
+        arsort($res);
+        $res = array_slice($res, 0, 10, true);
+        $this->xml->addChild('hints', json_encode($res));
+    }
+
+    public function searchResults()
+    {
+        $index = Zend_Search_Lucene::open(WS_BOOKS . '/search/' . $this->args['id']);
+
+        $terms = $this->getTerms($this->args['term']);
+
+        $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
+        foreach ($terms as $term) {
+            $query->addTerm(new Zend_Search_Lucene_Index_Term($term, 'contents'), null);
+        }
+
+        $hits = $index->find($query);
+        $res = array();
+        $terms = array();
+        foreach ($query->getQueryTerms() as $t) {
+            $terms[] = $t->text;
+            foreach ($index->termFreqs($t) as $doc_id => $f) {
+                $page = trim($index->getDocument($doc_id)->getField('url')->getUtf8Value(), "#");
+                if (!isset($res[$page])) {
+                    $res[$page] = 0;
+                }
+                $res[$page] += $f;
+            }
+        }
+        $this->xml->addChild('results', json_encode($res));
+        $this->xml->addChild('terms', implode(',', $terms));
+    }
+
+    protected function getTerms($term)
+    {
+        $term = trim($term, '*? ');
+        $term = mb_strtolower($term);
+        $term = cubeText::removeAccents($term);
+        $terms = explode(' ', $term);
+        return $terms;
+    }
+
+    public function printpdf()
+    {
+        $this->outputXML = false;
+        $this->exportpdf(true);
+    }
+
+    public function getPDFComplex($book = null, $range = null, $print = false)
+    {
+        global $core;
+
+        if (is_null($book)) {
+            $book = $this->callArgs[0];
+            $range = $this->callArgs[1];
+        }
+
+        $daoBook = new wsDAOBook($core->con);
+
+        if (is_int($book) || is_string($book)) {
+            $book = $daoBook->selectById($book);
+        }
+
+        if (is_null($book)) {
+            return false;
+        }
+
+        // Normalize range
+        $range = cubeArray::parseRange($range);
+
+        if ($k = array_search(0, $range)) {
+            $range[$k] = 1;
+        }
+        if ($k = array_search($book->parametres->pages + 1, $range)) {
+            $range[$k] = $r->pages;
+        }
+
+        $range = array_unique($range);
+        sort($range);
+
+
+        // Paths init
+        $baseDocument = $this->getPDFComplexBaseDocument($book);
+
+        if (!file_exists($baseDocument)) {
+            return false;
+        }
+
+        if (!count($range)) {
+            return false;
+        }
+
+        $destDir = WS_CACHE . '/exportpdf/' . $book->cid;
+        if (!file_exists($destDir)) {
+            mkdir($destDir, 0777, true);
+        }
+        $fname = md5($baseDocument) . '-' . md5(implode(',%ù', $range, ($print ? '1' : '0'))) . '.pdf';
+        $destFile = $destDir . '/' . $fname;
+        $destURL = '/fluidbook/cache/exportpdf/' . $book->cid . '/' . $fname;
+
+        // If result exists, don't make the pdf again
+        if (false && file_exists($destFile) && filemtime($destFile) > filemtime($baseDocument)) {
+
+        } else {
+
+            if ($print) {
+                $memoDest = $destFile;
+                $destFile = CubeIT_Files::tempnam();
+            }
+
+            if ($range[0] == 1 && count($range) == $book->parametres->pages && $range[$book->parametres->pages - 1] == $book->parametres->pages) {
+                `cp $baseDocument $destFile`;
+            } else {
+
+                // Prepare the command line
+                $l = array('A="' . $baseDocument . '"', 'cat');
+                foreach ($range as $page) {
+                    if ($page < 1 || $page > $book->parametres->pages) {
+                        continue;
+                    }
+                    $l[] = 'A' . $page;
+                }
+                $l[] = 'output';
+                $l[] = $destFile;
+
+                $args = implode(' ', $l);
+                // Execute the command line
+                $pdftk = new cubeCommandLine('pdftk');
+                $pdftk->setPath(CONVERTER_PATH);
+                $pdftk->setManualArg($args);
+                $pdftk->execute();
+            }
+
+            if ($print) {
+                `gs -dNoOutputFonts -sDEVICE=pdfwrite -o $memoDest $destFile`;
+            }
+        }
+
+        return array('url' => $destURL, 'file' => $destFile);
+    }
+
+    public function getPDFComplexBaseDocument($book)
+    {
+        global $core;
+        switch ($book->parametres->bookmarkUsePDF) {
+            case 'download':
+                return WS_BOOKS . '/working/' . $book->book_id . '/' . $book->parametres->pdfReplace;
+                break;
+            case 'thumbnails':
+                return WS_BOOKS . '/working/' . $book->book_id . '/' . $book->parametres->pdfThumbnails;
+                break;
+            case 'pages':
+            default:
+                $normal = WS_BOOKS . '/pdf/' . $book->book_id . '/original.pdf';
+                if (!file_exists($normal)) {
+                    $daoBook = new wsDAOBook($core->con);
+                    $pages = $daoBook->getPagesOfBook($book->book_id);
+                    $daoBook->compilePDF($book, $pages);
+                }
+                return $normal;
+                break;
+        }
+        return $res;
+    }
+
+    public function e()
+    {
+        $this->args['cid'] = $this->callArgs[0];
+        $this->args['range'] = $this->callArgs[1];
+        return $this->exportpdf(false, false);
+    }
+
+    public function ep()
+    {
+        $this->outputXML = false;
+        $this->args['cid'] = $this->callArgs[0];
+        $this->args['range'] = $this->callArgs[1];
+        return $this->exportpdf(false, true);
+    }
+
+    public function p()
+    {
+        $this->args['cid'] = $this->callArgs[0];
+        $this->args['range'] = $this->callArgs[1];
+        return $this->exportpdf(true, true);
+    }
+
+    public function exportpdf($print = false, $printfile = false)
+    {
+        global $core;
+
+        set_time_limit(0);
+
+        $dao = new wsDAOBook($core->con);
+        if (isset($this->args['cid'])) {
+            $book = $dao->selectByCid($this->args['cid']);
+        } else if ($this->args['id'] < 11202) {
+            $book = $dao->selectById($this->args['id']);
+        }
+
+        $dest = $this->getPDFComplex($book, $this->args['range'], $printfile);
+
+        if ($dest === false) {
+            http_response_code(404);
+            exit;
+        }
+
+        if (!$print) {
+            // Return the url of the resulting pdf
+            http::redirect($dest['url']);
+            exit;
+        } else {
+            $this->outputXML = false;
+            $res = '<!DOCTYPE html><html><head>
                                <meta http-equiv="X-UA-Compatible" content="IE=8" />
                                <script type="text/javascript" src="//code.jquery.com/jquery-1.8.2.min.js"></script>
                                <script type="text/javascript" src="/js/pdfprint.js"></script>
@@ -491,1034 +509,1034 @@ class wsServices extends cubeFlashGateway
                                <iframe id="pdf" name="pdff" src="' . $dest['url'] . '" width="100%" height="100%"></iframe>
                                </body>
                                </html>';
-                       ob_end_clean();
-                       echo $res;
-                       exit;
-               }
-       }
-
-       public function bulle()
-       {
-               global $core;
-               $e = explode('-', $this->args['catalogue'], 2);
-               if (count($e) == 2) {
-                       $catalogue = $e[1];
-               } else {
-                       $catalogue = $e[0];
-               }
-
-               $c = $core->con->openCursor('bulle');
-               $c->prenom = trim($this->args['prenom']);
-               $c->nom = trim($this->args['nom']);
-               $c->catalogue = trim($catalogue);
-               $c->email = trim($this->args['email']);
-               $c->date = TIME;
-               $c->insert();
-       }
-
-       public function getBulleList()
-       {
-               $user = 'bulle';
-               $pass = '23bu1l300';
-               $this->outputXML = false;
-               $ok = (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] == $user && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW'] == $pass);
-               if (!$ok) {
-                       header('WWW-Authenticate: Basic realm="Protected access"');
-                       header('HTTP/1.0 401 Unauthorized');
-                       ob_end_clean();
-                       header('Content-type: text/html');
-                       echo '<h1>Forbidden</h1>';
-                       exit;
-               } else {
-                       global $core;
-                       header('Content-type: text/csv');
-                       header('Content-Disposition: attachment; filename="inscriptions.csv"');
-                       $r = $core->con->select('SELECT * FROM bulle ORDER BY date');
-                       ob_end_clean();
-                       echo utf8_decode('"Prénom";"Nom";"E-mail";"Catalogue";"Date"') . "\n";
-                       while ($r->fetch()) {
-                               echo utf8_decode('"' . $r->prenom . '";"' . $r->nom . '";"' . $r->email . '";"' . $r->catalogue . '";"' . date('Y-m-d H:i', $r->date) . '"') . "\n";
-                       }
-                       exit;
-               }
-       }
-
-       public function grdfValidForm()
-       {
-               $notempty = array('civilite', 'prenom', 'nom', 'adresse', 'codepostal', 'ville', 'telephone', 'optin', 'connu', 'energie');
-               if ($this->args['id'] >= 12779) {
-                       $notempty = array_merge($notempty, array('logement', 'echeance', 'optin_partenaires'));
-               }
-               $error = false;
-               $errors = array();
-               foreach ($notempty as $f) {
-                       if (!isset($this->args[$f]) || is_null($this->args[$f]) || $this->args[$f] == 'null' || trim($this->args[$f]) == '') {
-                               $error = true;
-                               $errors[] = $f;
-                       }
-               }
-
-               /* if (!cubeMail::isEmail($this->args['email'])) {
-                 $error = true;
-                 $errors[] = 'email_valid';
-                 } */
-
-               $this->xml->addChild('ok', $error ? '0' : '1');
-               $this->xml->addChild('errors', implode(',', $errors));
-       }
-
-       public function grdfSendForm()
-       {
-               global $core;
-
-               if ($this->args['id'] == 11222) {
-                       $annee = 2013;
-               } else if ($this->args['id'] == 12235 || $this->args['id'] == 12779) {
-                       $annee = 2014;
-               }
-
-               $fields = array('Civilité' => 'civilite', 'Prénom' => 'prenom', 'Nom' => 'nom',
-                       'E-mail' => 'email', 'Adresse' => 'adresse', 'Code postal' => 'codepostal', 'Ville' => 'ville',
-                       'Téléphone' => 'telephone', 'Connu par' => 'connu', 'Energie' => 'energie',
-                       'Type de logement' => 'logement', 'Echéance travaux' => 'echeance',
-                       'Optin' => 'optin', 'Optin partenaires' => 'optin_partenaires', 'Coupons sélectionnés' => 'reflist');
-
-               $mail = new cubeMail();
-               $mail->charset = 'UTF-8';
-               $mail->subject = '[Chéquier avantages] Validation de coupon';
-               $mail->from = 'noreply@chequieravantages.fr';
-               $mail->replyTo = 'noreply@chequiavantages.fr';
-               $mail->to = 'projetrenogaz@grdf.fr';
-               $mail->to = 'pierric.soustre@external.grdf.fr';
-               $mail->to = 'caroline.gaulin@grdf.fr';
-               $mail->bcc = 'test@cubedesigners.com';
-               $body = '';
-
-               foreach ($fields as $k => $f) {
-                       $body .= $k . ' : ' . $this->args[$f] . "\r\n";
-                       $datas[$f] = $this->args[$f];
-               }
-               $datas['date'] = TIME;
-
-               $c = $core->con->openCursor('grdf' . $annee);
-               $c->datas = json_encode($datas);
-               $c->insert();
-
-               $mail->body = $body;
-               $mail->send();
-       }
-
-       public function grdfCoupon2015()
-       {
-               $id = $this->args['id'];
-
-               $this->outputXML = false;
-               $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
-
-               $xml = simplexml_load_file($couponsRoot . 'references.xml');
-               // Check hash
-               $hash = md5('ae' . $_GET['nom'] . '25' . $_GET['ref']);
-               if ($hash != $_GET['h']) {
-                       exit;
-               }
-               $refs = explode(',', $_GET['ref']);
-
-               $tmp = cubeFiles::tempnam();
-               $pdftk = new CubeIT_CommandLine('pdftk');
-               $pdftk->setPath(CONVERTER_PATH);
-               foreach ($refs as $ref) {
-                       $file = $couponsRoot . $ref . '.pdf';
-                       $pdftk->setArg(null, $file);
-               }
-               $pdftk->setArg(null, 'output');
-               $pdftk->setArg(null, $tmp);
-               $pdftk->execute();
-
-               header('Content-Type: application/pdf');
-               header('Content-Disposition: inline; filename="CouponChequier.pdf"');
-
-               readfile($tmp);
-               unlink($tmp);
-               exit;
-       }
-
-       public function grdfCoupon()
-       {
-
-               $id = $this->args['id'];
-
-               if ($id == 13372) {
-                       return $this->grdfCoupon2015();
-               }
-
-               $this->outputXML = false;
-               $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
-
-               $xml = simplexml_load_file($couponsRoot . 'references.xml');
-               // Check hash
-               $hash = md5('ae' . $_GET['nom'] . '25' . $_GET['ref']);
-               if ($hash != $_GET['h']) {
-                       exit;
-               }
-               $refs = explode(',', $_GET['ref']);
-               $pdf = new FPDI();
-               foreach ($refs as $ref) {
-
-                       foreach ($xml->xpath('//item[@reference=\'' . $ref . '\']') as $item) {
-                               $code = (string)$item['code'];
-                       }
-
-                       $file = $couponsRoot . $ref . '.pdf';
-
-                       $pdf->setSourceFile($file);
-                       $idx = $pdf->importPage(1);
-                       $pdf->AddPage();
-                       $pdf->useTemplate($idx);
-                       $pdf->SetXY(11, 12);
-                       $pdf->SetFont('Helvetica', '', 12);
-                       $pdf->SetTextColor(0, 0, 0);
-                       $pdf->Cell(180, 10, utf8_decode(trim($_GET['civilite'] . ' ' . $_GET['prenom'] . ' ' . $_GET['nom']) . ','));
-                       $pdf->SetXY(135, 203);
-                       $pdf->SetFont('Helvetica', 'B', 16);
-                       $pdf->Cell(50, 15, $code, 0, 1, "C");
-               }
-               $pdf->Output('coupon.pdf', 'I');
-       }
-
-       public function grdfExcel12568()
-       {
-               global $core;
-               $this->outputXML = false;
-
-               if (!isset($_GET['annee'])) {
-                       $_GET['annee'] = 2014;
-               }
-               $annee = $_GET['annee'];
-
-               if ($annee == 2013) {
-                       $id = 11222;
-               } elseif ($annee == 2014) {
-                       $id = 12779;
-               }
-
-
-               $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
-
-               $xml = simplexml_load_file($couponsRoot . 'references.xml');
-
-               $cols = array('Date' => 'date', 'Civilité' => 'civilite', 'Prénom' => 'prenom', 'Nom' => 'nom',
-                       'E-mail' => 'email', 'Adresse' => 'adresse', 'Code postal' => 'codepostal', 'Ville' => 'ville',
-                       'Téléphone' => 'telephone', 'Connu par' => 'connu', 'Energie' => 'energie', 'Type de logement' => 'logement',
-                       'Echéance travaux' => 'echeance',
-                       'Optin' => 'optin', 'Optin partenaires' => 'optin_partenaires');
-               $refs = array();
-               foreach ($xml->xpath('//item') as $i) {
-                       $refs[(string)$i['reference']] = $i['name'];
-               }
-
-               //
-               $h = '<table>';
-               $h .= '<tr>';
-               foreach ($cols as $l => $k) {
-                       $h .= '<th>' . utf8_decode($l) . '</th>';
-               }
-               foreach ($refs as $k => $l) {
-                       $h .= '<th>' . utf8_decode($l) . '</th>';
-               }
-               $h .= '</tr>';
-
-               $r = $core->con->select('SELECT * FROM grdf' . $annee);
-               while ($r->fetch()) {
-                       $d = json_decode($r->datas);
-                       $rl = explode(',', $d->reflist);
-                       $h .= '<tr>';
-                       foreach ($cols as $l => $k) {
-                               $v = $d->$k;
-                               if ($k == 'date') {
-                                       $v = date('Y-m-d H:i', $v);
-                               }
-                               $h .= '<td>' . utf8_decode($v) . '</td>';
-                       }
-                       foreach ($refs as $k => $l) {
-                               $v = in_array($k, $rl) ? '1' : '0';
-                               $h .= '<td>' . utf8_decode($v) . '</td>';
-                       }
-                       $h .= '</tr>';
-               }
-
-               $h .= '</table>';
-
-               if (!file_exists(ROOT . '/cache/12568/')) {
-                       mkdir(ROOT . '/cache/12568/', 0777, true);
-               }
-               file_put_contents(ROOT . '/cache/12568/chequieravantage.xls', $h);
-               http::redirect('/cache/12568/chequieravantage.xls');
-               exit;
-       }
-
-       public function proxy()
-       {
-               ob_end_clean();
-               fb(netHttp::quickGet($_GET['u'], 'php://output'));
-               $this->outputXML = false;
-       }
-
-       public function wescoRef()
-       {
-               global $core;
-
-               $this->args['ref'] = str_replace(' ', '', $this->args['ref']);
-               $ref = ltrim(substr($this->args['ref'], strlen($this->args['ref']) - 5), '0');
-
-               $r = $core->con->select('SELECT url FROM wescoref WHERE ref="' . $core->con->escape($ref) . '"');
-               if ($r->count()) {
-                       header('Location: ' . $r->url);
-                       exit;
-               }
-               $cache = ROOT . '/cache/wesco/' . $this->args['ref'];
-               if (file_exists($cache)) {
-                       header('Location: ' . file_get_contents($cache));
-                       exit;
-               }
-               $url = 'http://www.wesco-eshop.fr/catalogsearch/result/?q=' . $ref;
-               $doc = new DOMDocument();
-               $doc->loadHTML(file_get_contents($url));
-               $x = simplexml_import_dom($doc);
-               $xpath = $x->xpath('//a[@class="product-image"]');
-               if (count($xpath) > 0) {
-                       $r = $xpath[0]['href'];
-                       file_put_contents($cache, $r);
-                       header('Location: ' . $r);
-                       exit;
-               }
-               header('Location: http://www.wesco-eshop.fr');
-       }
-
-       public function wsref()
-       {
-               global $core;
-               $e = explode('|', $_GET['ref'], 2);
-               $type = trim($e[0]);
-               $ref = trim($e[1]);
-               $nzref = ltrim($e[1], '0');
-
-               $r = $core->con->select("SELECT * FROM wsref WHERE (ref='" . $core->con->escape($ref) . "' OR ref='" . $core->con->escape($nzref) . "') AND type='" . $core->con->escape($type) . "'");
-               if ($r->count()) {
-                       header('Location: ' . $r->url);
-               }
-       }
-
-       public function pierronRef()
-       {
-               global $core;
-               $ref = $this->args['ref'];
-
-               $r = $core->con->select('SELECT url FROM pierronref WHERE ref="' . $core->con->escape($ref) . '"');
-               if ($r->count()) {
-                       header('Location: ' . $r->url);
-                       exit;
-               }
-               header('Location: http://www.pierron.fr/');
-       }
-
-       public function flfRef()
-       {
-               global $core;
-
-               $r = $core->con->select('SELECT url FROM flfref WHERE ref="' . $core->con->escape($this->args['ref']) . '"');
-               if ($r->count()) {
-                       if (strpos($r->url, 'http') === 0) {
-                               header('Location: ' . $r->url);
-                       } else {
-                               header('Location: https://www.flf.fr' . $r->url);
-                       }
-                       exit;
-               }
-               $q = 'site:https://www.flf.fr/formation-professionnelle/ "Code stage : ' . $this->args['ref'] . '"';
-               header('Location: https://www.google.fr/search?q=' . urlencode($q) . '&ie=utf-8&oe=utf-8&btnI');
-               exit;
-       }
-
-       public function collection()
-       {
-               global $core;
-               $id = $this->callArgs[0];
-               $os = $this->callArgs[1];
-               $resolution = $this->callArgs[2];
-               $local = (isset($this->callArgs[3]) && $this->callArgs[3] != '0') ? $this->callArgs[3] : null;
-               $appver = (isset($this->callArgs[4]) && $this->callArgs[4]) ? $this->callArgs[4] : '1.0';
-
-               if (is_null($resolution)) {
-                       $resolution = 150;
-               }
-
-               if ($id == 4) {
-                       $resolution = 150;
-               }
-
-               $this->outputXML = false;
-               header('Content-type: application/json');
-
-               $appverf = str_replace('.', '_', $appver);
-
-               $cache = WS_COLLECTIONS . '/ws/' . $id . '.' . $os . '.' . $resolution . '.' . $appverf . '.json';
-               $update = WS_COLLECTIONS . '/ws/' . $id . '.' . $os . '.' . $resolution . '.' . $appverf . '.update';
-
-               if (file_exists($update) && filemtime(__FILE__) > filemtime($update)) {
-                       unlink($update);
-               }
-
-               if (!is_null($local) && file_exists($update) && file_get_contents($update) == $local) {
-                       echo 'false';
-                       exit;
-               }
-
-               $limit = TIME - 72000;
-               if (!file_exists($cache) || !file_exists($update) || filemtime($cache) < $limit) {
-                       $r = $core->con->select('SELECT *,online_' . $os . ' AS v FROM book_collection_compile WHERE online_' . $os . '!=\'\' AND collection_id=\'' . $core->con->escape($id) . '\' ORDER BY compile_date DESC');
-                       while ($r->fetch()) {
-                               if (version_compare($r->v, $appver, '>')) {
-                                       continue;
-                               }
-
-                               $version = $r->compile_date;
-
-                               $daoCollection = new wsDAOCollection($core->con);
-                               $collection = $daoCollection->selectById($id);
-
-                               $ns = $collection->settings['namespace'];
-
-                               $vcompo = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/composition.json';
-                               $composition = json_decode(file_get_contents($vcompo));
-
-                               $couvertures = array();
-                               $vroot = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/' . $os . '/';
-
-                               $publications = array();
-                               $langs = array();
-
-                               foreach ($composition as $k => $g) {
-                                       foreach ($g->publications as $l => $p) {
-                                               $publications[] = $p->id;
-                                       }
-                               }
-
-                               $daoBook = new wsDAOBook($core->con);
-                               $books = $daoBook->selectByIds($publications);
-
-                               $langsnames = array();
-                               foreach ($composition as $k => $g) {
-                                       foreach ($g->publications as $l => $p) {
-                                               $book = $books[$p->id];
-                                               $root = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/' . $os . '/' . $p->id;
-                                               $couv = $root . '/cover.jpg';
-                                               if (!file_exists($couv)) {
-                                                       $couv = $root . '/data/thumbnails/p1.jpg';
-                                               }
-                                               $couvertures[$p->id] = base64_encode(file_get_contents($couv));
-                                               $composition[$k]->publications[$l]->width = $book->parametres->width;
-                                               $composition[$k]->publications[$l]->height = $book->parametres->height;
-                                               $composition[$k]->publications[$l]->lang = $book->lang;
-                                               $completeLang = $book->lang;
-                                               if ($book->parametres->country) {
-                                                       $composition[$k]->publications[$l]->country = $book->parametres->country;
-                                                       $completeLang .= '_' . mb_strtoupper($book->parametres->country);
-                                               }
-                                               $composition[$k]->publications[$l]->completelang = $completeLang;
-
-                                               $langs[] = $completeLang;
-                                       }
-                               }
-
-                               $langs = array_unique($langs);
-
-                               $w2h = new wiki2xhtml();
-                               $w2h->setOpt('active_pre', 0);
-
-                               $contents = $collection->contents;
-                               foreach ($langs as $lang) {
-                                       $e = explode('_', $lang);
-                                       $langsnames[$lang] = cubeLang::getNameByCode($e[0]);
-                                       $contents[$lang]['apropos'] = $w2h->transform($contents[$lang]['apropos']);
-                                       if (isset($collection->theme['ad_' . $lang])) {
-                                               $ad = WS_COLLECTIONS . '/working/' . $id . '/' . $collection->theme['ad_' . $lang];
-                                               if (file_exists($ad)) {
-                                                       $contents[$lang]['ad'] = base64_encode(file_get_contents($ad));
-                                               }
-                                       }
-                               }
-
-
-                               $traductions = array();
-                               $r = $core->con->select('SELECT traductions,lang_id FROM langues WHERE lang_id IN(\'' . implode('\',\'', $langs) . '\')');
-                               while ($r->fetch()) {
-                                       $traductions[$r->lang_id] = json_decode($r->traductions);
-                               }
-
-                               $theme = $collection->theme;
-                               $settings = $collection->settings;
-
-                               $d = array('id' => $id, 'res' => $resolution, 'ns' => $ns, 'langs' => $langs, 'langnames' => $langsnames, 'time' => $version, 'datas' => $composition, 'couvertures' => $couvertures, 'traductions' => $traductions, 'contents' => $contents, 'theme' => $theme, 'settings' => $settings);
-                               if (!$collection->settings['offline']) {
-                                       $d = array_merge($d, $this->_getManifest($publications, '/fluidbook/collections/versions/' . $id . '/' . $version . '/' . $os, $books, $resolution));
-                               }
-
-                               $dao = new wsDAOCollection($core->con);
-                               $col = $dao->selectById($id);
-
-                               $json = json_encode($d);
-                               file_put_contents($cache, $json);
-                               file_put_contents($update, $version);
-
-                               break;
-                       }
-               } else {
-                       $d = json_decode(file_get_contents($cache), true);
-               }
-
-               $force = false;
-               if (!is_null($local)) {
-                       $lcompo = WS_COLLECTIONS . '/versions/' . $id . '/' . $local . '/composition.json';
-                       if (!file_exists($lcompo)) {
-                               $force = true;
-                       }
-               }
-
-
-               $d['forceUpdate'] = $force;
-               echo json_encode($d);
-               exit;
-       }
-
-       protected function _getManifest($publications, $dir, $books, $resolution)
-       {
-               global $core;
-               $res = array();
-               $res['manifest'] = array('assetRoot' => 'https://workshop.fluidbook.com' . $dir . '/', 'autoDownload' => false);
-               $res['manifestPub'] = array();
-
-               $bundles = array();
-
-               $removeFromRelative = realpath(ROOT . '/' . $dir) . '/';
-               $daoTheme = new wsDAOTheme($core->con);
-
-               foreach ($publications as $p) {
-                       $res['manifestPub'][$p] = array('assetRoot' => 'https://workshop.fluidbook.com' . $dir . '/', 'autoDownload' => true);
-
-                       $r = $p . '/';
-                       $iterator = CubeIT_Files::getRecursiveDirectoryIterator(ROOT . $dir . '/' . $p);
-                       $book = $books[$p];
-                       $reso = $resolution;
-
-                       $theme = $daoTheme->selectById($book->theme);
-                       $orders = $this->_getBundles($book);
-                       $regexp = $this->_getRegExpManifest($r, $book, $theme, $reso);
-                       $reg = $regexp['reg'];
-                       $exclude = $regexp['exclude'];
-
-                       $b = array();
-
-                       foreach ($iterator as $k => $f) {
-                               if ($f->isDir()) {
-                                       continue;
-                               }
-                               $path = str_replace($removeFromRelative, '', $k);
-
-                               foreach ($exclude as $e) {
-                                       if (preg_match('|' . $e . '|', $path)) {
-                                               continue 2;
-                                       }
-                               }
-
-                               $order = $this->_inFirstManifest($path, $orders, $reg);
-
-                               if (!isset($b[$order])) {
-                                       $b[$order] = array();
-                               }
-
-                               $b[$order][] = $path;
-                       }
-
-                       $bundles[] = array('name' => 'p_' . $p, 'contents' => $b['loading']);
-                       $res['manifestPub'][$p]['bundles'] = array();
-
-                       foreach ($b as $name => $contents) {
-                               if ($name == 'loading') {
-                                       continue;
-                               }
-                               $k = array_search($name, $orders);
-                               $res['manifestPub'][$p]['bundles'][$k] = array('name' => $name, 'contents' => $contents);
-                       }
-                       ksort($res['manifestPub'][$p]['bundles']);
-                       $res['manifestPub'][$p]['bundles'] = array_values($res['manifestPub'][$p]['bundles']);
-               }
-
-               $res['manifest']['bundles'] = $bundles;
-               return $res;
-       }
-
-       protected function _getRegExpManifest($r, $book, $theme, $resolution)
-       {
-               $reg = array();
-               $reg['loading'] = array('^' . $r . 'style/(.*).css$', '^' . $r . 'index.html$', '^' . $r . 'data/style/(.*)$', '^' . $r . 'data/(.*).js$', '^' . $r . 'data/images/' . $theme->parametres->logoLoader . '$', '^' . $r . 'style/fonts/(.*).ttf$', '^' . $r . 'data/images/interface-down.svg$', '^' . $r . 'plugins/(.*)$');
-               $reg['extras'] = array('^' . $r . 'data/links/(.*).mp4$', '^' . $r . 'data/links/(.*).ogv$', '^' . $r . 'data/links/(.*).webm$', '^' . $r . 'data/(.*).pdf$', '^' . $r . 'cover.jpg$');
-               $reg['thumbnails'] = array('^' . $r . 'data/thumbnails/p(\d+).jpg$');
-               for ($i = 1; $i <= $book->parametres->pages; $i++) {
-                       $var = 'content_' . $i;
-                       $reg[$var] = array('^' . $r . 'data/background/\d+/t' . $i . '.jpg$', '^' . $r . 'data/contents/p' . $i . '.svg$');
-               }
-               $reg['urgents'] = array('^' . $r . 'images/(.*)$', '^' . $r . 'data/images/(.*)$', '^' . $r . 'data/links/(.*).jpg$', '^' . $r . 'data/links/(.*).png$');
-               $reg['loading'] = array_merge($reg['loading'], $reg['urgents']);
-
-
-               $exclude = array();
-               if ($resolution == 150) {
-                       $er = 300;
-               } else {
-                       $er = 150;
-               }
-               $exclude[] = '^' . $r . 'data/background/' . $er . '/t\d+.jpg$';
-               return array('reg' => $reg, 'exclude' => $exclude);
-       }
-
-       protected function _inFirstManifest($p, $orders, $reg)
-       {
-               foreach ($orders as $list) {
-                       foreach ($reg[$list] as $v) {
-                               if (preg_match('|' . $v . '|', $p)) {
-                                       return $list;
-                               }
-                       }
-               }
-               return $list;
-       }
-
-       protected function _getBundles($book)
-       {
-               $res = array(0 => 'loading', 20001 => 'extras', 20000 => 'thumbnails', 1 => 'urgents');
-               for ($i = 1; $i <= $book->parametres->pages; $i++) {
-                       $k = 2 + $i;
-                       $res[$k] = 'content_' . $i;
-               }
-               return $res;
-       }
-
-       public function collectionPushRegister()
-       {
-               global $core;
-
-               $c = $core->con->openCursor('book_collection_push');
-               $c->collection_id = $_POST['id'];
-               $c->token = $_POST['token'];
-               $c->datas = $_POST['datas'];
-               $c->platform = $_POST['platform'];
-               $c->locale = $_POST['locale'];
-               $c->last_visit = TIME;
-               try {
-                       $c->insert();
-               } catch (Exception $e) {
-                       $c->update('WHERE collection_id=\'' . $core->con->escape($_POST['id']) . '\' AND token=\'' . $core->con->escape($_POST['token']) . '\'');
-               }
-       }
-
-       public function getFluidbookPage()
-       {
-               global $core;
-               $this->outputXML = false;
-
-               $id = $this->callArgs[0];
-               $page = $this->callArgs[1];
-
-               $dao = new wsDAOBook($core->con);
-               $pages = $dao->getPagesOfBook($id, false);
-               $p = $pages[$page];
-
-               $file = wsDocument::getDir($p['document_id']) . '/html/t150-' . $p['document_page'] . '.jpg';
-
-               ob_end_clean();
-               cubeHTTP::contentType($file);
-               echo file_get_contents($file);
-               die;
-       }
-
-       public function orderRemarkable()
-       {
-               try {
-                       $html = file_get_contents(WS_FILES . '/services/remarkableorder.html');
-                       $billing_address = $_POST['details']['firstname'] . ' ' . $_POST['details']['lastname'] . '<br><br>' . $_POST['details']['billing_company'] . " <br>" . $_POST['details']['billing_address'] . '<br>' . $_POST['details']['billing_zip'] . ' ' . $_POST['details']['billing_city'];
-
-                       $params = array('company', 'address', 'zip', 'city');
-                       foreach ($params as $param) {
-                               if (trim($_POST['details']['shipping_' . $param]) == '') {
-                                       $_POST['details']['shipping_' . $param] = $_POST['details']['billing_' . $param];
-                               }
-                       }
-                       $shipping_address = $_POST['details']['shipping_company'] . " <br>" . $_POST['details']['shipping_address'] . '<br>' . $_POST['details']['shipping_zip'] . ' ' . $_POST['details']['shipping_city'];
-
-                       if ($_POST['fpv'] == 'hidden') {
-                               $fraisport = '';
-                       } else {
-                               $fraisport = '<tr>';
-                               $fraisport .= '<td></td><td colspan="3" bgcolor="#f2f2f2" align="right"><font size="1" face="Verdana" color="#1F497D">Frais de livraison</font></td>';
-                               $fraisport .= '<td bgcolor="#f2f2f2" align="right"><font size="1" face="Verdana" color="#1F497D">' . $_POST['fpv'] . '</font></td>';
-                               $fraisport .= '</tr>';
-                       }
-
-                       $lignes = [];
-                       foreach ($_POST['items'] as $item) {
-                               $ligne = '<tr>';
-                               $ligne .= '<td bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[0] . '</font></td>';
-                               $ligne .= '<td bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D"><b>' . $item[1] . '</b></font></td>';
-                               $ligne .= '<td align="right" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[2] . '</font></td>';
-                               $ligne .= '<td align="center" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[3] . '</font></td>';
-                               $ligne .= '<td align="right" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[4] . '</font></td>';
-                               $ligne .= '</tr>';
-                               $lignes[] = $ligne;
-                       }
-
-                       $lignes = implode('', $lignes);
-                       $totalttc = $_POST['totalttc'];
-
-                       $replace = ['billing_address', 'shipping_address', 'totalttc', 'fraisport', 'lignes'];
-                       foreach ($replace as $item) {
-                               if (isset($$item)) {
-                                       $html = str_replace('$' . $item, str_replace('€', '&euro;', $$item), $html);
-                               }
-                       }
-
-                       $mail = new cubeMail();
-                       $mail->type = "html";
-                       $mail->from = 'transfert-fluidbook@remarkable.fr';
-                       $mail->to = $_POST['details']['email'];
-                       $mail->bcc = 'transfert-fluidbook@remarkable.fr';
-                       $mail->bcc = 'test@cubedesigners.com';
-                       $mail->subject = '[Remarkable] Votre commande';
-                       $mail->body = $html;
-                       $mail->send();
-               } catch (Exception $e) {
-
-               }
-       }
-
-       public function bourbonSuggestion()
-       {
-               global $core;
-               try {
-                       $mail = new PhpMimeMailParser\Parser();
-                       $mail->setText(base64_decode($_POST['email']));
-                       $from = $mail->getAddresses('from');
-                       $from = $from[0]['address'];
-
-
-                       $attachments = $mail->getAttachments();
-                       foreach ($attachments as $k => $attachment) {
-                               if ($attachment->getFilename() !== 'suggestion.txt') {
-                                       continue;
-                               }
-                               $suggestion = $attachment->getContent();
-                               $parts = explode('/|!/', $suggestion);
-                               $c = $core->con->openCursor('bourbon_suggestions');
-                               $c->type = $parts[2];
-                               $c->suggestion = $parts[3];
-                               $c->date = round($parts[1] / 1000);
-                               $c->from = $from;
-                               $c->page = $parts[0];
-                               $c->insert();
-
-                               break;
-                       }
-                       file_put_contents('/tmp/bourbonSuggestion', print_r($mail, true));
-                       file_put_contents('/tmp/bourbonSuggestionAttach', print_r($attachments, true));
-               } catch (Exception $e) {
-                       file_put_contents('/tmp/bourbonSuggestion', print_r($e, true));
-               }
-
-               $body = "Hello,\r\nThis automatic message confirms that your suggestion has been received.\r\nPlease do not reply to this e-mail,\r\nThank you";
-
-               $ack = new Zend_Mail();
-               $ack->setFrom('no-reply@bourbonoffshore.com');
-               $ack->setSubject('Suggestion acknowledge');
-               $ack->setBodyText($body);
-
-               $ack->addTo($from);
-               $ack->send();
-       }
-
-       public function downloadBourbonSuggestions58412()
-       {
-               global $core;
-               $this->outputXML = false;
-               $r = $core->con->select('SELECT * FROM bourbon_suggestions ORDER BY date DESC');
-               $excel = new PHPExcel();
-               $excel->setActiveSheetIndex(0);
-               $defaultStyle = $excel->getDefaultStyle();
-               $defaultStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
-               $defaultStyle->getAlignment()->setWrapText(true);
-               $s = $excel->getActiveSheet();
-               $s->setTitle('Suggestions');
-               $s->setCellValueByColumnAndRow(0, 1, 'Sender');
-               $s->setCellValueByColumnAndRow(1, 1, 'Date');
-               $s->setCellValueByColumnAndRow(2, 1, 'Type');
-               $s->setCellValueByColumnAndRow(3, 1, 'Page');
-               $s->setCellValueByColumnAndRow(4, 1, 'Suggestion content');
-
-               $line = 2;
-               while ($r->fetch()) {
-                       $date = date('Y-m-d H:i:s', round($r->date));
-
-                       $s->setCellValueByColumnAndRow(0, $line, $r->from);
-                       $s->setCellValueByColumnAndRow(1, $line, $date);
-                       $s->setCellValueByColumnAndRow(2, $line, $r->type);
-                       $s->setCellValueByColumnAndRow(3, $line, $r->page);
-                       $s->setCellValueByColumnAndRow(4, $line, $r->suggestion);
-
-                       $line++;
-               }
-
-               for ($i = 0; $i <= 4; $i++) {
-                       $s->getColumnDimensionByColumn($i)->setAutoSize(true);
-               }
-
-
-               header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
-               header('Content-Disposition: attachment;filename="fluidbook-suggestions.xlsx"');
-               header('Cache-Control: max-age=0');
-               // If you're serving to IE 9, then the following may be needed
-               header('Cache-Control: max-age=1');
-               // If you're serving to IE over SSL, then the following may be needed
-               header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
-               header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
-               header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
-               header('Pragma: public'); // HTTP/1.0
-
-               $writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007');
-               ob_end_clean();
-               $writer->save('php://output');
-               exit;
-
-       }
-
-       public static function subscribe()
-       {
-               if (isset($_POST['form'])) {
-                       switch ($_POST['form']) {
-                               case 'avery':
-                                       self::_subscribeAvery();
-                                       break;
-                               default:
-                                       break;
-                       }
-               }
-       }
-
-       public function proxypinterest()
-       {
-               $this->outputXML = false;
-
-               header('Content-type: application/rss+xml');
-               $url = 'https://www.pinterest.com/' . urlencode($this->args['url']);
-               ob_end_clean();
-               $c = file_get_contents($url);
-               echo $c;
-               exit;
-       }
-
-       public static function _subscribeAvery()
-       {
-               $api = new CubeIT_Services_Mailchimp('5d00b7e601cf1948cb95de06630878ae-us19');
-               $data = [
-                       'email' => $_POST['email'],
-                       'COMPANY' => $_POST['company'],
-                       'ACTIVITY' => $_POST['activity'],
-                       'COUNTRY' => $_POST['country'],
-                       'OPTIN' => $_POST['subscribe'],
-                       'LANGUAGE' => $_POST['locale']
-               ];
-               $api->subscribe('33699982ca', $data);
-
-       }
-
-       public static function gpuSeparators()
-       {
-               return ['Direct3D', 'DirectX', 'OpenGL', 'Graphics'];
-       }
-
-       public static function gpuTests()
-       {
-               return [546, 544, 680, 632];
-       }
-
-       public function gup()
-       {
-               global $core;
-
-               $this->outputXML = false;
-
-               $rgpu = $gpu = trim(base64_decode($_GET['gup']));
-
-               if (strstr($gpu, 'ANGLE (')) {
-                       if (preg_match('/\((.*)\)/', $gpu, $matches)) {
-                               if (strlen($matches[1]) > 8) {
-                                       $gpu = $matches[1];
-                               }
-                       }
-
-                       $split = self::gpuSeparators();
-
-                       foreach ($split as $item) {
-                               $e = explode($item, $gpu);
-                               if (count($e) > 1) {
-                                       $gpu = $e[0];
-                                       break;
-                               }
-                       }
-               }
-               $version = 40;
-
-
-               $gpu = trim($gpu);
-
-               $c = $core->con->openCursor('gpu_log');
-               $c->gpu = $gpu;
-               $c->rgpu = $rgpu;
-               $c->date = time();
-               $c->insert();
-
-               $r = $core->con->select("SELECT * FROM gpu WHERE version=" . $version . " AND (gpu='" . $core->con->escape($gpu) . "' OR rgpu='" . $core->con->escape($rgpu) . "')");
-               header('Content-type: text/plain');
-
-               if ($r->isEmpty()) {
-                       $search = self::searchGFXDevice($c->gpu, $rgpu, $version);
-
-                       $doc = new DOMDocument();
-                       $doc->strictErrorChecking = FALSE;
-                       $doc->loadHTML($search['body']);
-                       $xml = simplexml_import_dom($doc);
-
-                       $links = $xml->xpath("//a");
-                       foreach ($links as $link) {
-                               $h = parse_url($link['href']);
-                               if (trim($h['path']) != 'subtest_results_of_device.jsp') {
-                                       continue;
-                               }
-                               parse_str($h['query'], $data);
-                               if (!isset($data['id'])) {
-                                       continue;
-                               }
-                               $values = $link->xpath('span[@class=\'value\']');
-                               if (!count($values)) {
-                                       continue;
-                               }
-                               $value = trim((string)$values[0]);
-                               if ($value == 'N/A') {
-                                       continue;
-                               }
-                               if (!stristr($value, 'frame')) {
-                                       continue;
-                               }
-                               $res[$data['id']] = intval($value);
-                       }
-
-                       $scoreids = self::gpuTests();
-                       $num = 0;
-                       $score = 0;
-                       foreach ($scoreids as $scoreid) {
-                               if (!isset($res[$scoreid])) {
-                                       continue;
-                               }
-                               $num++;
-                               $score += $res[$scoreid];
-                       }
-
-                       $score /= $num;
-                       $score = round($score);
-
-                       $c = $core->con->openCursor('gpu');
-                       $c->gpu = $gpu;
-                       $c->rgpu = $rgpu;
-                       $c->page = $search['body'];
-                       $c->url = $search['url'];
-                       $c->version = $version;
-                       $c->benchmark = serialize($res);
-                       $c->score = $score;
-                       $c->date = time();
-                       $c->insert();
-               } else {
-                       $r->fetch();
-                       $score = $r->score;
-               }
-               ob_end_clean();
-               echo $score;
-               exit;
-       }
-
-       public static function searchGFXDevice($gpu, $raw, $gfxVersion = 40)
-       {
-               // First intention direct request
-               $ugpu = urlencode($gpu);
-               $url = 'https://gfxbench.com/device.jsp?benchmark=gfx' . $gfxVersion . '&hwname=' . $ugpu . '&D=' . $ugpu . '&testgroup=graphics&var=median';
-
-               $f = self::_curlRequest($url);
-               if (!stristr($f, '<p class="label1">No such device')) {
-                       return ['url' => $url, 'body' => $f];
-               }
-
-               // Second intention is to use the search engine to lookup the gpu by name
-               $tests = self::gpuTests();
-               $cacheLimit = max(time() - 21600, filemtime(__FILE__));
-               foreach ($tests as $test) {
-
-                       // Test cache file
-                       $cacheTest = WS_CACHE . '/gfxbench/test_' . $test;
-                       if (!file_exists($cacheTest) || filemtime($cacheTest) < $cacheLimit) {
-                               $f = self::_curlRequest('https://gfxbench.com/result.jsp?benchmark=gfx' . $gfxVersion . '&test=' . $test . '&order=median&base=device');
-
-                               // Search database is stored as js variables in code
-                               $variables = ['gpuNameLookup', 'deviceId', 'gpuName'];
-                               $v = [];
-                               foreach ($variables as $variable) {
-                                       if (preg_match('/var ' . $variable . ' = ([^;]*);/ui', $f, $matches)) {
-                                               eval('$v[$variable] = ' . $matches[1] . ';');
-                                       }
-                               }
-                               file_put_contents($cacheTest, serialize($v));
-                       } else {
-                               $v = unserialize(file_get_contents($cacheTest));
-                       }
-
-                       $key = null;
-                       foreach ($v['gpuNameLookup'] as $k => $item) {
-                               if (stristr($item, $raw)) {
-                                       $key = $k;
-                                       break;
-                               }
-                       }
-
-                       // If we get a device id, we have the the device page :)
-                       if (null !== $key) {
-                               $deviceKey = array_search($key, $v['gpuName']);
-                               $deviceId = $v['deviceId'][$deviceKey];
-                               $url = 'https://gfxbench.com/device.jsp?benchmark=gfx' . $gfxVersion . '&did=' . $deviceId;
-                               $f = self::_curlRequest($url);
-                               if (!stristr($f, '<p class="label1">No such device')) {
-                                       return ['url' => $url, 'body' => $f];
-                               }
-                       }
-               }
-               return ['url' => '', 'body' => ''];
-       }
-
-
-       protected static function _curlRequest($url)
-       {
-               $ch = curl_init();
-               curl_setopt($ch, CURLOPT_URL, $url);
-               curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-               curl_setopt($ch, CURLOPT_VERBOSE, 1);
-               curl_setopt($ch, CURLOPT_HEADER, 1);
-               curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0');
-               $response = curl_exec($ch);
-               $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
-               return substr($response, $header_size);
-       }
+            ob_end_clean();
+            echo $res;
+            exit;
+        }
+    }
+
+    public function bulle()
+    {
+        global $core;
+        $e = explode('-', $this->args['catalogue'], 2);
+        if (count($e) == 2) {
+            $catalogue = $e[1];
+        } else {
+            $catalogue = $e[0];
+        }
+
+        $c = $core->con->openCursor('bulle');
+        $c->prenom = trim($this->args['prenom']);
+        $c->nom = trim($this->args['nom']);
+        $c->catalogue = trim($catalogue);
+        $c->email = trim($this->args['email']);
+        $c->date = TIME;
+        $c->insert();
+    }
+
+    public function getBulleList()
+    {
+        $user = 'bulle';
+        $pass = '23bu1l300';
+        $this->outputXML = false;
+        $ok = (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] == $user && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW'] == $pass);
+        if (!$ok) {
+            header('WWW-Authenticate: Basic realm="Protected access"');
+            header('HTTP/1.0 401 Unauthorized');
+            ob_end_clean();
+            header('Content-type: text/html');
+            echo '<h1>Forbidden</h1>';
+            exit;
+        } else {
+            global $core;
+            header('Content-type: text/csv');
+            header('Content-Disposition: attachment; filename="inscriptions.csv"');
+            $r = $core->con->select('SELECT * FROM bulle ORDER BY date');
+            ob_end_clean();
+            echo utf8_decode('"Prénom";"Nom";"E-mail";"Catalogue";"Date"') . "\n";
+            while ($r->fetch()) {
+                echo utf8_decode('"' . $r->prenom . '";"' . $r->nom . '";"' . $r->email . '";"' . $r->catalogue . '";"' . date('Y-m-d H:i', $r->date) . '"') . "\n";
+            }
+            exit;
+        }
+    }
+
+    public function grdfValidForm()
+    {
+        $notempty = array('civilite', 'prenom', 'nom', 'adresse', 'codepostal', 'ville', 'telephone', 'optin', 'connu', 'energie');
+        if ($this->args['id'] >= 12779) {
+            $notempty = array_merge($notempty, array('logement', 'echeance', 'optin_partenaires'));
+        }
+        $error = false;
+        $errors = array();
+        foreach ($notempty as $f) {
+            if (!isset($this->args[$f]) || is_null($this->args[$f]) || $this->args[$f] == 'null' || trim($this->args[$f]) == '') {
+                $error = true;
+                $errors[] = $f;
+            }
+        }
+
+        /* if (!cubeMail::isEmail($this->args['email'])) {
+          $error = true;
+          $errors[] = 'email_valid';
+          } */
+
+        $this->xml->addChild('ok', $error ? '0' : '1');
+        $this->xml->addChild('errors', implode(',', $errors));
+    }
+
+    public function grdfSendForm()
+    {
+        global $core;
+
+        if ($this->args['id'] == 11222) {
+            $annee = 2013;
+        } else if ($this->args['id'] == 12235 || $this->args['id'] == 12779) {
+            $annee = 2014;
+        }
+
+        $fields = array('Civilité' => 'civilite', 'Prénom' => 'prenom', 'Nom' => 'nom',
+            'E-mail' => 'email', 'Adresse' => 'adresse', 'Code postal' => 'codepostal', 'Ville' => 'ville',
+            'Téléphone' => 'telephone', 'Connu par' => 'connu', 'Energie' => 'energie',
+            'Type de logement' => 'logement', 'Echéance travaux' => 'echeance',
+            'Optin' => 'optin', 'Optin partenaires' => 'optin_partenaires', 'Coupons sélectionnés' => 'reflist');
+
+        $mail = new cubeMail();
+        $mail->charset = 'UTF-8';
+        $mail->subject = '[Chéquier avantages] Validation de coupon';
+        $mail->from = 'noreply@chequieravantages.fr';
+        $mail->replyTo = 'noreply@chequiavantages.fr';
+        $mail->to = 'projetrenogaz@grdf.fr';
+        $mail->to = 'pierric.soustre@external.grdf.fr';
+        $mail->to = 'caroline.gaulin@grdf.fr';
+        $mail->bcc = 'test@cubedesigners.com';
+        $body = '';
+
+        foreach ($fields as $k => $f) {
+            $body .= $k . ' : ' . $this->args[$f] . "\r\n";
+            $datas[$f] = $this->args[$f];
+        }
+        $datas['date'] = TIME;
+
+        $c = $core->con->openCursor('grdf' . $annee);
+        $c->datas = json_encode($datas);
+        $c->insert();
+
+        $mail->body = $body;
+        $mail->send();
+    }
+
+    public function grdfCoupon2015()
+    {
+        $id = $this->args['id'];
+
+        $this->outputXML = false;
+        $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
+
+        $xml = simplexml_load_file($couponsRoot . 'references.xml');
+        // Check hash
+        $hash = md5('ae' . $_GET['nom'] . '25' . $_GET['ref']);
+        if ($hash != $_GET['h']) {
+            exit;
+        }
+        $refs = explode(',', $_GET['ref']);
+
+        $tmp = cubeFiles::tempnam();
+        $pdftk = new CubeIT_CommandLine('pdftk');
+        $pdftk->setPath(CONVERTER_PATH);
+        foreach ($refs as $ref) {
+            $file = $couponsRoot . $ref . '.pdf';
+            $pdftk->setArg(null, $file);
+        }
+        $pdftk->setArg(null, 'output');
+        $pdftk->setArg(null, $tmp);
+        $pdftk->execute();
+
+        header('Content-Type: application/pdf');
+        header('Content-Disposition: inline; filename="CouponChequier.pdf"');
+
+        readfile($tmp);
+        unlink($tmp);
+        exit;
+    }
+
+    public function grdfCoupon()
+    {
+
+        $id = $this->args['id'];
+
+        if ($id == 13372) {
+            return $this->grdfCoupon2015();
+        }
+
+        $this->outputXML = false;
+        $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
+
+        $xml = simplexml_load_file($couponsRoot . 'references.xml');
+        // Check hash
+        $hash = md5('ae' . $_GET['nom'] . '25' . $_GET['ref']);
+        if ($hash != $_GET['h']) {
+            exit;
+        }
+        $refs = explode(',', $_GET['ref']);
+        $pdf = new FPDI();
+        foreach ($refs as $ref) {
+
+            foreach ($xml->xpath('//item[@reference=\'' . $ref . '\']') as $item) {
+                $code = (string)$item['code'];
+            }
+
+            $file = $couponsRoot . $ref . '.pdf';
+
+            $pdf->setSourceFile($file);
+            $idx = $pdf->importPage(1);
+            $pdf->AddPage();
+            $pdf->useTemplate($idx);
+            $pdf->SetXY(11, 12);
+            $pdf->SetFont('Helvetica', '', 12);
+            $pdf->SetTextColor(0, 0, 0);
+            $pdf->Cell(180, 10, utf8_decode(trim($_GET['civilite'] . ' ' . $_GET['prenom'] . ' ' . $_GET['nom']) . ','));
+            $pdf->SetXY(135, 203);
+            $pdf->SetFont('Helvetica', 'B', 16);
+            $pdf->Cell(50, 15, $code, 0, 1, "C");
+        }
+        $pdf->Output('coupon.pdf', 'I');
+    }
+
+    public function grdfExcel12568()
+    {
+        global $core;
+        $this->outputXML = false;
+
+        if (!isset($_GET['annee'])) {
+            $_GET['annee'] = 2014;
+        }
+        $annee = $_GET['annee'];
+
+        if ($annee == 2013) {
+            $id = 11222;
+        } elseif ($annee == 2014) {
+            $id = 12779;
+        }
+
+
+        $couponsRoot = WS_BOOKS . '/working/' . $id . '/commerce/';
+
+        $xml = simplexml_load_file($couponsRoot . 'references.xml');
+
+        $cols = array('Date' => 'date', 'Civilité' => 'civilite', 'Prénom' => 'prenom', 'Nom' => 'nom',
+            'E-mail' => 'email', 'Adresse' => 'adresse', 'Code postal' => 'codepostal', 'Ville' => 'ville',
+            'Téléphone' => 'telephone', 'Connu par' => 'connu', 'Energie' => 'energie', 'Type de logement' => 'logement',
+            'Echéance travaux' => 'echeance',
+            'Optin' => 'optin', 'Optin partenaires' => 'optin_partenaires');
+        $refs = array();
+        foreach ($xml->xpath('//item') as $i) {
+            $refs[(string)$i['reference']] = $i['name'];
+        }
+
+        //
+        $h = '<table>';
+        $h .= '<tr>';
+        foreach ($cols as $l => $k) {
+            $h .= '<th>' . utf8_decode($l) . '</th>';
+        }
+        foreach ($refs as $k => $l) {
+            $h .= '<th>' . utf8_decode($l) . '</th>';
+        }
+        $h .= '</tr>';
+
+        $r = $core->con->select('SELECT * FROM grdf' . $annee);
+        while ($r->fetch()) {
+            $d = json_decode($r->datas);
+            $rl = explode(',', $d->reflist);
+            $h .= '<tr>';
+            foreach ($cols as $l => $k) {
+                $v = $d->$k;
+                if ($k == 'date') {
+                    $v = date('Y-m-d H:i', $v);
+                }
+                $h .= '<td>' . utf8_decode($v) . '</td>';
+            }
+            foreach ($refs as $k => $l) {
+                $v = in_array($k, $rl) ? '1' : '0';
+                $h .= '<td>' . utf8_decode($v) . '</td>';
+            }
+            $h .= '</tr>';
+        }
+
+        $h .= '</table>';
+
+        if (!file_exists(ROOT . '/cache/12568/')) {
+            mkdir(ROOT . '/cache/12568/', 0777, true);
+        }
+        file_put_contents(ROOT . '/cache/12568/chequieravantage.xls', $h);
+        http::redirect('/cache/12568/chequieravantage.xls');
+        exit;
+    }
+
+    public function proxy()
+    {
+        ob_end_clean();
+        fb(netHttp::quickGet($_GET['u'], 'php://output'));
+        $this->outputXML = false;
+    }
+
+    public function wescoRef()
+    {
+        global $core;
+
+        $this->args['ref'] = str_replace(' ', '', $this->args['ref']);
+        $ref = ltrim(substr($this->args['ref'], strlen($this->args['ref']) - 5), '0');
+
+        $r = $core->con->select('SELECT url FROM wescoref WHERE ref="' . $core->con->escape($ref) . '"');
+        if ($r->count()) {
+            header('Location: ' . $r->url);
+            exit;
+        }
+        $cache = ROOT . '/cache/wesco/' . $this->args['ref'];
+        if (file_exists($cache)) {
+            header('Location: ' . file_get_contents($cache));
+            exit;
+        }
+        $url = 'http://www.wesco-eshop.fr/catalogsearch/result/?q=' . $ref;
+        $doc = new DOMDocument();
+        $doc->loadHTML(file_get_contents($url));
+        $x = simplexml_import_dom($doc);
+        $xpath = $x->xpath('//a[@class="product-image"]');
+        if (count($xpath) > 0) {
+            $r = $xpath[0]['href'];
+            file_put_contents($cache, $r);
+            header('Location: ' . $r);
+            exit;
+        }
+        header('Location: http://www.wesco-eshop.fr');
+    }
+
+    public function wsref()
+    {
+        global $core;
+        $e = explode('|', $_GET['ref'], 2);
+        $type = trim($e[0]);
+        $ref = trim($e[1]);
+        $nzref = ltrim($e[1], '0');
+
+        $r = $core->con->select("SELECT * FROM wsref WHERE (ref='" . $core->con->escape($ref) . "' OR ref='" . $core->con->escape($nzref) . "') AND type='" . $core->con->escape($type) . "'");
+        if ($r->count()) {
+            header('Location: ' . $r->url);
+        }
+    }
+
+    public function pierronRef()
+    {
+        global $core;
+        $ref = $this->args['ref'];
+
+        $r = $core->con->select('SELECT url FROM pierronref WHERE ref="' . $core->con->escape($ref) . '"');
+        if ($r->count()) {
+            header('Location: ' . $r->url);
+            exit;
+        }
+        header('Location: http://www.pierron.fr/');
+    }
+
+    public function flfRef()
+    {
+        global $core;
+
+        $r = $core->con->select('SELECT url FROM flfref WHERE ref="' . $core->con->escape($this->args['ref']) . '"');
+        if ($r->count()) {
+            if (strpos($r->url, 'http') === 0) {
+                header('Location: ' . $r->url);
+            } else {
+                header('Location: https://www.flf.fr' . $r->url);
+            }
+            exit;
+        }
+        $q = 'site:https://www.flf.fr/formation-professionnelle/ "Code stage : ' . $this->args['ref'] . '"';
+        header('Location: https://www.google.fr/search?q=' . urlencode($q) . '&ie=utf-8&oe=utf-8&btnI');
+        exit;
+    }
+
+    public function collection()
+    {
+        global $core;
+        $id = $this->callArgs[0];
+        $os = $this->callArgs[1];
+        $resolution = $this->callArgs[2];
+        $local = (isset($this->callArgs[3]) && $this->callArgs[3] != '0') ? $this->callArgs[3] : null;
+        $appver = (isset($this->callArgs[4]) && $this->callArgs[4]) ? $this->callArgs[4] : '1.0';
+
+        if (is_null($resolution)) {
+            $resolution = 150;
+        }
+
+        if ($id == 4) {
+            $resolution = 150;
+        }
+
+        $this->outputXML = false;
+        header('Content-type: application/json');
+
+        $appverf = str_replace('.', '_', $appver);
+
+        $cache = WS_COLLECTIONS . '/ws/' . $id . '.' . $os . '.' . $resolution . '.' . $appverf . '.json';
+        $update = WS_COLLECTIONS . '/ws/' . $id . '.' . $os . '.' . $resolution . '.' . $appverf . '.update';
+
+        if (file_exists($update) && filemtime(__FILE__) > filemtime($update)) {
+            unlink($update);
+        }
+
+        if (!is_null($local) && file_exists($update) && file_get_contents($update) == $local) {
+            echo 'false';
+            exit;
+        }
+
+        $limit = TIME - 72000;
+        if (!file_exists($cache) || !file_exists($update) || filemtime($cache) < $limit) {
+            $r = $core->con->select('SELECT *,online_' . $os . ' AS v FROM book_collection_compile WHERE online_' . $os . '!=\'\' AND collection_id=\'' . $core->con->escape($id) . '\' ORDER BY compile_date DESC');
+            while ($r->fetch()) {
+                if (version_compare($r->v, $appver, '>')) {
+                    continue;
+                }
+
+                $version = $r->compile_date;
+
+                $daoCollection = new wsDAOCollection($core->con);
+                $collection = $daoCollection->selectById($id);
+
+                $ns = $collection->settings['namespace'];
+
+                $vcompo = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/composition.json';
+                $composition = json_decode(file_get_contents($vcompo));
+
+                $couvertures = array();
+                $vroot = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/' . $os . '/';
+
+                $publications = array();
+                $langs = array();
+
+                foreach ($composition as $k => $g) {
+                    foreach ($g->publications as $l => $p) {
+                        $publications[] = $p->id;
+                    }
+                }
+
+                $daoBook = new wsDAOBook($core->con);
+                $books = $daoBook->selectByIds($publications);
+
+                $langsnames = array();
+                foreach ($composition as $k => $g) {
+                    foreach ($g->publications as $l => $p) {
+                        $book = $books[$p->id];
+                        $root = WS_COLLECTIONS . '/versions/' . $id . '/' . $version . '/' . $os . '/' . $p->id;
+                        $couv = $root . '/cover.jpg';
+                        if (!file_exists($couv)) {
+                            $couv = $root . '/data/thumbnails/p1.jpg';
+                        }
+                        $couvertures[$p->id] = base64_encode(file_get_contents($couv));
+                        $composition[$k]->publications[$l]->width = $book->parametres->width;
+                        $composition[$k]->publications[$l]->height = $book->parametres->height;
+                        $composition[$k]->publications[$l]->lang = $book->lang;
+                        $completeLang = $book->lang;
+                        if ($book->parametres->country) {
+                            $composition[$k]->publications[$l]->country = $book->parametres->country;
+                            $completeLang .= '_' . mb_strtoupper($book->parametres->country);
+                        }
+                        $composition[$k]->publications[$l]->completelang = $completeLang;
+
+                        $langs[] = $completeLang;
+                    }
+                }
+
+                $langs = array_unique($langs);
+
+                $w2h = new wiki2xhtml();
+                $w2h->setOpt('active_pre', 0);
+
+                $contents = $collection->contents;
+                foreach ($langs as $lang) {
+                    $e = explode('_', $lang);
+                    $langsnames[$lang] = cubeLang::getNameByCode($e[0]);
+                    $contents[$lang]['apropos'] = $w2h->transform($contents[$lang]['apropos']);
+                    if (isset($collection->theme['ad_' . $lang])) {
+                        $ad = WS_COLLECTIONS . '/working/' . $id . '/' . $collection->theme['ad_' . $lang];
+                        if (file_exists($ad)) {
+                            $contents[$lang]['ad'] = base64_encode(file_get_contents($ad));
+                        }
+                    }
+                }
+
+
+                $traductions = array();
+                $r = $core->con->select('SELECT traductions,lang_id FROM langues WHERE lang_id IN(\'' . implode('\',\'', $langs) . '\')');
+                while ($r->fetch()) {
+                    $traductions[$r->lang_id] = json_decode($r->traductions);
+                }
+
+                $theme = $collection->theme;
+                $settings = $collection->settings;
+
+                $d = array('id' => $id, 'res' => $resolution, 'ns' => $ns, 'langs' => $langs, 'langnames' => $langsnames, 'time' => $version, 'datas' => $composition, 'couvertures' => $couvertures, 'traductions' => $traductions, 'contents' => $contents, 'theme' => $theme, 'settings' => $settings);
+                if (!$collection->settings['offline']) {
+                    $d = array_merge($d, $this->_getManifest($publications, '/fluidbook/collections/versions/' . $id . '/' . $version . '/' . $os, $books, $resolution));
+                }
+
+                $dao = new wsDAOCollection($core->con);
+                $col = $dao->selectById($id);
+
+                $json = json_encode($d);
+                file_put_contents($cache, $json);
+                file_put_contents($update, $version);
+
+                break;
+            }
+        } else {
+            $d = json_decode(file_get_contents($cache), true);
+        }
+
+        $force = false;
+        if (!is_null($local)) {
+            $lcompo = WS_COLLECTIONS . '/versions/' . $id . '/' . $local . '/composition.json';
+            if (!file_exists($lcompo)) {
+                $force = true;
+            }
+        }
+
+
+        $d['forceUpdate'] = $force;
+        echo json_encode($d);
+        exit;
+    }
+
+    protected function _getManifest($publications, $dir, $books, $resolution)
+    {
+        global $core;
+        $res = array();
+        $res['manifest'] = array('assetRoot' => 'https://workshop.fluidbook.com' . $dir . '/', 'autoDownload' => false);
+        $res['manifestPub'] = array();
+
+        $bundles = array();
+
+        $removeFromRelative = realpath(ROOT . '/' . $dir) . '/';
+        $daoTheme = new wsDAOTheme($core->con);
+
+        foreach ($publications as $p) {
+            $res['manifestPub'][$p] = array('assetRoot' => 'https://workshop.fluidbook.com' . $dir . '/', 'autoDownload' => true);
+
+            $r = $p . '/';
+            $iterator = CubeIT_Files::getRecursiveDirectoryIterator(ROOT . $dir . '/' . $p);
+            $book = $books[$p];
+            $reso = $resolution;
+
+            $theme = $daoTheme->selectById($book->theme);
+            $orders = $this->_getBundles($book);
+            $regexp = $this->_getRegExpManifest($r, $book, $theme, $reso);
+            $reg = $regexp['reg'];
+            $exclude = $regexp['exclude'];
+
+            $b = array();
+
+            foreach ($iterator as $k => $f) {
+                if ($f->isDir()) {
+                    continue;
+                }
+                $path = str_replace($removeFromRelative, '', $k);
+
+                foreach ($exclude as $e) {
+                    if (preg_match('|' . $e . '|', $path)) {
+                        continue 2;
+                    }
+                }
+
+                $order = $this->_inFirstManifest($path, $orders, $reg);
+
+                if (!isset($b[$order])) {
+                    $b[$order] = array();
+                }
+
+                $b[$order][] = $path;
+            }
+
+            $bundles[] = array('name' => 'p_' . $p, 'contents' => $b['loading']);
+            $res['manifestPub'][$p]['bundles'] = array();
+
+            foreach ($b as $name => $contents) {
+                if ($name == 'loading') {
+                    continue;
+                }
+                $k = array_search($name, $orders);
+                $res['manifestPub'][$p]['bundles'][$k] = array('name' => $name, 'contents' => $contents);
+            }
+            ksort($res['manifestPub'][$p]['bundles']);
+            $res['manifestPub'][$p]['bundles'] = array_values($res['manifestPub'][$p]['bundles']);
+        }
+
+        $res['manifest']['bundles'] = $bundles;
+        return $res;
+    }
+
+    protected function _getRegExpManifest($r, $book, $theme, $resolution)
+    {
+        $reg = array();
+        $reg['loading'] = array('^' . $r . 'style/(.*).css$', '^' . $r . 'index.html$', '^' . $r . 'data/style/(.*)$', '^' . $r . 'data/(.*).js$', '^' . $r . 'data/images/' . $theme->parametres->logoLoader . '$', '^' . $r . 'style/fonts/(.*).ttf$', '^' . $r . 'data/images/interface-down.svg$', '^' . $r . 'plugins/(.*)$');
+        $reg['extras'] = array('^' . $r . 'data/links/(.*).mp4$', '^' . $r . 'data/links/(.*).ogv$', '^' . $r . 'data/links/(.*).webm$', '^' . $r . 'data/(.*).pdf$', '^' . $r . 'cover.jpg$');
+        $reg['thumbnails'] = array('^' . $r . 'data/thumbnails/p(\d+).jpg$');
+        for ($i = 1; $i <= $book->parametres->pages; $i++) {
+            $var = 'content_' . $i;
+            $reg[$var] = array('^' . $r . 'data/background/\d+/t' . $i . '.jpg$', '^' . $r . 'data/contents/p' . $i . '.svg$');
+        }
+        $reg['urgents'] = array('^' . $r . 'images/(.*)$', '^' . $r . 'data/images/(.*)$', '^' . $r . 'data/links/(.*).jpg$', '^' . $r . 'data/links/(.*).png$');
+        $reg['loading'] = array_merge($reg['loading'], $reg['urgents']);
+
+
+        $exclude = array();
+        if ($resolution == 150) {
+            $er = 300;
+        } else {
+            $er = 150;
+        }
+        $exclude[] = '^' . $r . 'data/background/' . $er . '/t\d+.jpg$';
+        return array('reg' => $reg, 'exclude' => $exclude);
+    }
+
+    protected function _inFirstManifest($p, $orders, $reg)
+    {
+        foreach ($orders as $list) {
+            foreach ($reg[$list] as $v) {
+                if (preg_match('|' . $v . '|', $p)) {
+                    return $list;
+                }
+            }
+        }
+        return $list;
+    }
+
+    protected function _getBundles($book)
+    {
+        $res = array(0 => 'loading', 20001 => 'extras', 20000 => 'thumbnails', 1 => 'urgents');
+        for ($i = 1; $i <= $book->parametres->pages; $i++) {
+            $k = 2 + $i;
+            $res[$k] = 'content_' . $i;
+        }
+        return $res;
+    }
+
+    public function collectionPushRegister()
+    {
+        global $core;
+
+        $c = $core->con->openCursor('book_collection_push');
+        $c->collection_id = $_POST['id'];
+        $c->token = $_POST['token'];
+        $c->datas = $_POST['datas'];
+        $c->platform = $_POST['platform'];
+        $c->locale = $_POST['locale'];
+        $c->last_visit = TIME;
+        try {
+            $c->insert();
+        } catch (Exception $e) {
+            $c->update('WHERE collection_id=\'' . $core->con->escape($_POST['id']) . '\' AND token=\'' . $core->con->escape($_POST['token']) . '\'');
+        }
+    }
+
+    public function getFluidbookPage()
+    {
+        global $core;
+        $this->outputXML = false;
+
+        $id = $this->callArgs[0];
+        $page = $this->callArgs[1];
+
+        $dao = new wsDAOBook($core->con);
+        $pages = $dao->getPagesOfBook($id, false);
+        $p = $pages[$page];
+
+        $file = wsDocument::getDir($p['document_id']) . '/html/t150-' . $p['document_page'] . '.jpg';
+
+        ob_end_clean();
+        cubeHTTP::contentType($file);
+        echo file_get_contents($file);
+        die;
+    }
+
+    public function orderRemarkable()
+    {
+        try {
+            $html = file_get_contents(WS_FILES . '/services/remarkableorder.html');
+            $billing_address = $_POST['details']['firstname'] . ' ' . $_POST['details']['lastname'] . '<br><br>' . $_POST['details']['billing_company'] . " <br>" . $_POST['details']['billing_address'] . '<br>' . $_POST['details']['billing_zip'] . ' ' . $_POST['details']['billing_city'];
+
+            $params = array('company', 'address', 'zip', 'city');
+            foreach ($params as $param) {
+                if (trim($_POST['details']['shipping_' . $param]) == '') {
+                    $_POST['details']['shipping_' . $param] = $_POST['details']['billing_' . $param];
+                }
+            }
+            $shipping_address = $_POST['details']['shipping_company'] . " <br>" . $_POST['details']['shipping_address'] . '<br>' . $_POST['details']['shipping_zip'] . ' ' . $_POST['details']['shipping_city'];
+
+            if ($_POST['fpv'] == 'hidden') {
+                $fraisport = '';
+            } else {
+                $fraisport = '<tr>';
+                $fraisport .= '<td></td><td colspan="3" bgcolor="#f2f2f2" align="right"><font size="1" face="Verdana" color="#1F497D">Frais de livraison</font></td>';
+                $fraisport .= '<td bgcolor="#f2f2f2" align="right"><font size="1" face="Verdana" color="#1F497D">' . $_POST['fpv'] . '</font></td>';
+                $fraisport .= '</tr>';
+            }
+
+            $lignes = [];
+            foreach ($_POST['items'] as $item) {
+                $ligne = '<tr>';
+                $ligne .= '<td bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[0] . '</font></td>';
+                $ligne .= '<td bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D"><b>' . $item[1] . '</b></font></td>';
+                $ligne .= '<td align="right" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[2] . '</font></td>';
+                $ligne .= '<td align="center" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[3] . '</font></td>';
+                $ligne .= '<td align="right" bgcolor="#ebecee"><font size="1" face="Verdana" color="#1F497D">' . $item[4] . '</font></td>';
+                $ligne .= '</tr>';
+                $lignes[] = $ligne;
+            }
+
+            $lignes = implode('', $lignes);
+            $totalttc = $_POST['totalttc'];
+
+            $replace = ['billing_address', 'shipping_address', 'totalttc', 'fraisport', 'lignes'];
+            foreach ($replace as $item) {
+                if (isset($$item)) {
+                    $html = str_replace('$' . $item, str_replace('€', '&euro;', $$item), $html);
+                }
+            }
+
+            $mail = new cubeMail();
+            $mail->type = "html";
+            $mail->from = 'transfert-fluidbook@remarkable.fr';
+            $mail->to = $_POST['details']['email'];
+            $mail->bcc = 'transfert-fluidbook@remarkable.fr';
+            $mail->bcc = 'test@cubedesigners.com';
+            $mail->subject = '[Remarkable] Votre commande';
+            $mail->body = $html;
+            $mail->send();
+        } catch (Exception $e) {
+
+        }
+    }
+
+    public function bourbonSuggestion()
+    {
+        global $core;
+        try {
+            $mail = new PhpMimeMailParser\Parser();
+            $mail->setText(base64_decode($_POST['email']));
+            $from = $mail->getAddresses('from');
+            $from = $from[0]['address'];
+
+
+            $attachments = $mail->getAttachments();
+            foreach ($attachments as $k => $attachment) {
+                if ($attachment->getFilename() !== 'suggestion.txt') {
+                    continue;
+                }
+                $suggestion = $attachment->getContent();
+                $parts = explode('/|!/', $suggestion);
+                $c = $core->con->openCursor('bourbon_suggestions');
+                $c->type = $parts[2];
+                $c->suggestion = $parts[3];
+                $c->date = round($parts[1] / 1000);
+                $c->from = $from;
+                $c->page = $parts[0];
+                $c->insert();
+
+                break;
+            }
+            file_put_contents('/tmp/bourbonSuggestion', print_r($mail, true));
+            file_put_contents('/tmp/bourbonSuggestionAttach', print_r($attachments, true));
+        } catch (Exception $e) {
+            file_put_contents('/tmp/bourbonSuggestion', print_r($e, true));
+        }
+
+        $body = "Hello,\r\nThis automatic message confirms that your suggestion has been received.\r\nPlease do not reply to this e-mail,\r\nThank you";
+
+        $ack = new Zend_Mail();
+        $ack->setFrom('no-reply@bourbonoffshore.com');
+        $ack->setSubject('Suggestion acknowledge');
+        $ack->setBodyText($body);
+
+        $ack->addTo($from);
+        $ack->send();
+    }
+
+    public function downloadBourbonSuggestions58412()
+    {
+        global $core;
+        $this->outputXML = false;
+        $r = $core->con->select('SELECT * FROM bourbon_suggestions ORDER BY date DESC');
+        $excel = new PHPExcel();
+        $excel->setActiveSheetIndex(0);
+        $defaultStyle = $excel->getDefaultStyle();
+        $defaultStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
+        $defaultStyle->getAlignment()->setWrapText(true);
+        $s = $excel->getActiveSheet();
+        $s->setTitle('Suggestions');
+        $s->setCellValueByColumnAndRow(0, 1, 'Sender');
+        $s->setCellValueByColumnAndRow(1, 1, 'Date');
+        $s->setCellValueByColumnAndRow(2, 1, 'Type');
+        $s->setCellValueByColumnAndRow(3, 1, 'Page');
+        $s->setCellValueByColumnAndRow(4, 1, 'Suggestion content');
+
+        $line = 2;
+        while ($r->fetch()) {
+            $date = date('Y-m-d H:i:s', round($r->date));
+
+            $s->setCellValueByColumnAndRow(0, $line, $r->from);
+            $s->setCellValueByColumnAndRow(1, $line, $date);
+            $s->setCellValueByColumnAndRow(2, $line, $r->type);
+            $s->setCellValueByColumnAndRow(3, $line, $r->page);
+            $s->setCellValueByColumnAndRow(4, $line, $r->suggestion);
+
+            $line++;
+        }
+
+        for ($i = 0; $i <= 4; $i++) {
+            $s->getColumnDimensionByColumn($i)->setAutoSize(true);
+        }
+
+
+        header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+        header('Content-Disposition: attachment;filename="fluidbook-suggestions.xlsx"');
+        header('Cache-Control: max-age=0');
+        // If you're serving to IE 9, then the following may be needed
+        header('Cache-Control: max-age=1');
+        // If you're serving to IE over SSL, then the following may be needed
+        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
+        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
+        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
+        header('Pragma: public'); // HTTP/1.0
+
+        $writer = PHPExcel_IOFactory::createWriter($excel, 'Excel2007');
+        ob_end_clean();
+        $writer->save('php://output');
+        exit;
+
+    }
+
+    public static function subscribe()
+    {
+        if (isset($_POST['form'])) {
+            switch ($_POST['form']) {
+                case 'avery':
+                    self::_subscribeAvery();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    public function proxypinterest()
+    {
+        $this->outputXML = false;
+
+        header('Content-type: application/rss+xml');
+        $url = 'https://www.pinterest.com/' . urlencode($this->args['url']);
+        ob_end_clean();
+        $c = file_get_contents($url);
+        echo $c;
+        exit;
+    }
+
+    public static function _subscribeAvery()
+    {
+        $api = new CubeIT_Services_Mailchimp('5d00b7e601cf1948cb95de06630878ae-us19');
+        $data = [
+            'email' => $_POST['email'],
+            'COMPANY' => $_POST['company'],
+            'ACTIVITY' => $_POST['activity'],
+            'COUNTRY' => $_POST['country'],
+            'OPTIN' => $_POST['subscribe'],
+            'LANGUAGE' => $_POST['locale']
+        ];
+        $api->subscribe('33699982ca', $data);
+
+    }
+
+    public static function gpuSeparators()
+    {
+        return ['Direct3D', 'DirectX', 'OpenGL', 'Graphics'];
+    }
+
+    public static function gpuTests()
+    {
+        return [546, 544, 680, 632];
+    }
+
+    public function gup()
+    {
+        global $core;
+
+        $this->outputXML = false;
+
+        $rgpu = $gpu = trim(base64_decode($_GET['gup']));
+
+        if (strstr($gpu, 'ANGLE (')) {
+            if (preg_match('/\((.*)\)/', $gpu, $matches)) {
+                if (strlen($matches[1]) > 8) {
+                    $gpu = $matches[1];
+                }
+            }
+
+            $split = self::gpuSeparators();
+
+            foreach ($split as $item) {
+                $e = explode($item, $gpu);
+                if (count($e) > 1) {
+                    $gpu = $e[0];
+                    break;
+                }
+            }
+        }
+        $version = 40;
+
+
+        $gpu = trim($gpu);
+
+        $c = $core->con->openCursor('gpu_log');
+        $c->gpu = $gpu;
+        $c->rgpu = $rgpu;
+        $c->date = time();
+        $c->insert();
+
+        $r = $core->con->select("SELECT * FROM gpu WHERE version=" . $version . " AND (gpu='" . $core->con->escape($gpu) . "' OR rgpu='" . $core->con->escape($rgpu) . "')");
+        header('Content-type: text/plain');
+
+        if ($r->isEmpty()) {
+            $search = self::searchGFXDevice($c->gpu, $rgpu, $version);
+
+            $doc = new DOMDocument();
+            $doc->strictErrorChecking = FALSE;
+            $doc->loadHTML($search['body']);
+            $xml = simplexml_import_dom($doc);
+
+            $links = $xml->xpath("//a");
+            foreach ($links as $link) {
+                $h = parse_url($link['href']);
+                if (trim($h['path']) != 'subtest_results_of_device.jsp') {
+                    continue;
+                }
+                parse_str($h['query'], $data);
+                if (!isset($data['id'])) {
+                    continue;
+                }
+                $values = $link->xpath('span[@class=\'value\']');
+                if (!count($values)) {
+                    continue;
+                }
+                $value = trim((string)$values[0]);
+                if ($value == 'N/A') {
+                    continue;
+                }
+                if (!stristr($value, 'frame')) {
+                    continue;
+                }
+                $res[$data['id']] = intval($value);
+            }
+
+            $scoreids = self::gpuTests();
+            $num = 0;
+            $score = 0;
+            foreach ($scoreids as $scoreid) {
+                if (!isset($res[$scoreid])) {
+                    continue;
+                }
+                $num++;
+                $score += $res[$scoreid];
+            }
+
+            $score /= $num;
+            $score = round($score);
+
+            $c = $core->con->openCursor('gpu');
+            $c->gpu = $gpu;
+            $c->rgpu = $rgpu;
+            $c->page = $search['body'];
+            $c->url = $search['url'];
+            $c->version = $version;
+            $c->benchmark = serialize($res);
+            $c->score = $score;
+            $c->date = time();
+            $c->insert();
+        } else {
+            $r->fetch();
+            $score = $r->score;
+        }
+        ob_end_clean();
+        echo $score;
+        exit;
+    }
+
+    public static function searchGFXDevice($gpu, $raw, $gfxVersion = 40)
+    {
+        // First intention direct request
+        $ugpu = urlencode($gpu);
+        $url = 'https://gfxbench.com/device.jsp?benchmark=gfx' . $gfxVersion . '&hwname=' . $ugpu . '&D=' . $ugpu . '&testgroup=graphics&var=median';
+
+        $f = self::_curlRequest($url);
+        if (!stristr($f, '<p class="label1">No such device')) {
+            return ['url' => $url, 'body' => $f];
+        }
+
+        // Second intention is to use the search engine to lookup the gpu by name
+        $tests = self::gpuTests();
+        $cacheLimit = max(time() - 21600, filemtime(__FILE__));
+        foreach ($tests as $test) {
+
+            // Test cache file
+            $cacheTest = WS_CACHE . '/gfxbench/test_' . $test;
+            if (!file_exists($cacheTest) || filemtime($cacheTest) < $cacheLimit) {
+                $f = self::_curlRequest('https://gfxbench.com/result.jsp?benchmark=gfx' . $gfxVersion . '&test=' . $test . '&order=median&base=device');
+
+                // Search database is stored as js variables in code
+                $variables = ['gpuNameLookup', 'deviceId', 'gpuName'];
+                $v = [];
+                foreach ($variables as $variable) {
+                    if (preg_match('/var ' . $variable . ' = ([^;]*);/ui', $f, $matches)) {
+                        eval('$v[$variable] = ' . $matches[1] . ';');
+                    }
+                }
+                file_put_contents($cacheTest, serialize($v));
+            } else {
+                $v = unserialize(file_get_contents($cacheTest));
+            }
+
+            $key = null;
+            foreach ($v['gpuNameLookup'] as $k => $item) {
+                if (stristr($item, $raw)) {
+                    $key = $k;
+                    break;
+                }
+            }
+
+            // If we get a device id, we have the the device page :)
+            if (null !== $key) {
+                $deviceKey = array_search($key, $v['gpuName']);
+                $deviceId = $v['deviceId'][$deviceKey];
+                $url = 'https://gfxbench.com/device.jsp?benchmark=gfx' . $gfxVersion . '&did=' . $deviceId;
+                $f = self::_curlRequest($url);
+                if (!stristr($f, '<p class="label1">No such device')) {
+                    return ['url' => $url, 'body' => $f];
+                }
+            }
+        }
+        return ['url' => '', 'body' => ''];
+    }
+
+
+    protected static function _curlRequest($url)
+    {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_VERBOSE, 1);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0');
+        $response = curl_exec($ch);
+        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+        return substr($response, $header_size);
+    }
 }