aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc
diff options
context:
space:
mode:
authorNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-08-07 10:41:26 -0700
committerNicolas Lœuillet <nicolas.loeuillet@gmail.com>2013-08-07 10:41:26 -0700
commit01c0e050ad8eca54f115dfa21db99e4f61ab7ca7 (patch)
treee1bdacb68b3a56644f4525974844dd954d6e3c6b /inc
parentda2c5d6fc33587c775a7d8a738c2c18de41f83b2 (diff)
parent339d510fda0a43b08981309f7540acedf3a4976c (diff)
downloadwallabag-01c0e050ad8eca54f115dfa21db99e4f61ab7ca7.tar.gz
wallabag-01c0e050ad8eca54f115dfa21db99e4f61ab7ca7.tar.zst
wallabag-01c0e050ad8eca54f115dfa21db99e4f61ab7ca7.zip
Merge pull request #104 from inthepoche/twig
Twig version on dev branch
Diffstat (limited to 'inc')
-rw-r--r--inc/3rdparty/Encoding.php (renamed from inc/Encoding.php)0
-rw-r--r--inc/3rdparty/JSLikeHTMLElement.php (renamed from inc/JSLikeHTMLElement.php)0
-rw-r--r--inc/3rdparty/Readability.php (renamed from inc/Readability.php)0
-rw-r--r--inc/3rdparty/Session.class.php (renamed from inc/Session.class.php)2
-rwxr-xr-x[-rw-r--r--]inc/3rdparty/class.messages.php (renamed from inc/class.messages.php)460
-rw-r--r--inc/3rdparty/paginator.php202
-rw-r--r--inc/3rdparty/simple_html_dom.php (renamed from inc/simple_html_dom.php)0
-rw-r--r--inc/MyTool.class.php265
-rw-r--r--inc/config.php73
-rw-r--r--inc/functions.php400
-rw-r--r--inc/poche/Database.class.php216
-rw-r--r--inc/poche/Poche.class.php429
-rw-r--r--inc/poche/Tools.class.php226
-rw-r--r--inc/poche/Url.class.php94
-rw-r--r--inc/poche/User.class.php50
-rw-r--r--inc/poche/config.inc.php61
-rw-r--r--inc/poche/pochePictures.php110
-rw-r--r--inc/rain.tpl.class.php1043
-rw-r--r--inc/store/file.class.php51
-rw-r--r--inc/store/sqlite.class.php202
-rw-r--r--inc/store/store.class.php63
21 files changed, 1619 insertions, 2328 deletions
diff --git a/inc/Encoding.php b/inc/3rdparty/Encoding.php
index 577763b4..577763b4 100644
--- a/inc/Encoding.php
+++ b/inc/3rdparty/Encoding.php
diff --git a/inc/JSLikeHTMLElement.php b/inc/3rdparty/JSLikeHTMLElement.php
index 238ba8a8..238ba8a8 100644
--- a/inc/JSLikeHTMLElement.php
+++ b/inc/3rdparty/JSLikeHTMLElement.php
diff --git a/inc/Readability.php b/inc/3rdparty/Readability.php
index e1e8738b..e1e8738b 100644
--- a/inc/Readability.php
+++ b/inc/3rdparty/Readability.php
diff --git a/inc/Session.class.php b/inc/3rdparty/Session.class.php
index eff924cc..3162f507 100644
--- a/inc/Session.class.php
+++ b/inc/3rdparty/Session.class.php
@@ -93,7 +93,7 @@ class Session
93 // Force logout 93 // Force logout
94 public static function logout() 94 public static function logout()
95 { 95 {
96 unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass']); 96 unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
97 } 97 }
98 98
99 // Make sure user is logged in. 99 // Make sure user is logged in.
diff --git a/inc/class.messages.php b/inc/3rdparty/class.messages.php
index 6d515bf6..e60bd3a1 100644..100755
--- a/inc/class.messages.php
+++ b/inc/3rdparty/class.messages.php
@@ -1,231 +1,231 @@
1<?php 1<?php
2//-------------------------------------------------------------------------------------------------- 2//--------------------------------------------------------------------------------------------------
3// Session-Based Flash Messages v1.0 3// Session-Based Flash Messages v1.0
4// Copyright 2012 Mike Everhart (http://mikeeverhart.net) 4// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
5// 5//
6// Licensed under the Apache License, Version 2.0 (the "License"); 6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License. 7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at 8// You may obtain a copy of the License at
9// 9//
10// http://www.apache.org/licenses/LICENSE-2.0 10// http://www.apache.org/licenses/LICENSE-2.0
11// 11//
12// Unless required by applicable law or agreed to in writing, software 12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS, 13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and 15// See the License for the specific language governing permissions and
16// limitations under the License. 16// limitations under the License.
17// 17//
18//------------------------------------------------------------------------------ 18//------------------------------------------------------------------------------
19// Description: 19// Description:
20//------------------------------------------------------------------------------ 20//------------------------------------------------------------------------------
21// 21//
22// Stores messages in Session data to be easily retrieved later on. 22// Stores messages in Session data to be easily retrieved later on.
23// This class includes four different types of messages: 23// This class includes four different types of messages:
24// - Success 24// - Success
25// - Error 25// - Error
26// - Warning 26// - Warning
27// - Information 27// - Information
28// 28//
29// See README for basic usage instructions, or see samples/index.php for more advanced samples 29// See README for basic usage instructions, or see samples/index.php for more advanced samples
30// 30//
31//-------------------------------------------------------------------------------------------------- 31//--------------------------------------------------------------------------------------------------
32// Changelog 32// Changelog
33//-------------------------------------------------------------------------------------------------- 33//--------------------------------------------------------------------------------------------------
34// 34//
35// 2011-05-15 - v1.0 - Initial Version 35// 2011-05-15 - v1.0 - Initial Version
36// 36//
37//-------------------------------------------------------------------------------------------------- 37//--------------------------------------------------------------------------------------------------
38 38
39class Messages { 39class Messages {
40 40
41 //----------------------------------------------------------------------------------------------- 41 //-----------------------------------------------------------------------------------------------
42 // Class Variables 42 // Class Variables
43 //----------------------------------------------------------------------------------------------- 43 //-----------------------------------------------------------------------------------------------
44 var $msgId; 44 var $msgId;
45 var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' ); 45 var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
46 var $msgClass = 'messages'; 46 var $msgClass = 'messages';
47 var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'></a>\n%s</div>\n"; 47 var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
48 var $msgBefore = '<p>'; 48 var $msgBefore = '<p>';
49 var $msgAfter = "</p>\n"; 49 var $msgAfter = "</p>\n";
50 50
51 51
52 /** 52 /**
53 * Constructor 53 * Constructor
54 * @author Mike Everhart 54 * @author Mike Everhart
55 */ 55 */
56 public function __construct() { 56 public function __construct() {
57 57
58 // Generate a unique ID for this user and session 58 // Generate a unique ID for this user and session
59 $this->msgId = md5(uniqid()); 59 $this->msgId = md5(uniqid());
60 60
61 // Create the session array if it doesnt already exist 61 // Create the session array if it doesnt already exist
62 if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); 62 if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
63 63
64 } 64 }
65 65
66 /** 66 /**
67 * Add a message to the queue 67 * Add a message to the queue
68 * 68 *
69 * @author Mike Everhart 69 * @author Mike Everhart
70 * 70 *
71 * @param string $type The type of message to add 71 * @param string $type The type of message to add
72 * @param string $message The message 72 * @param string $message The message
73 * @param string $redirect_to (optional) If set, the user will be redirected to this URL 73 * @param string $redirect_to (optional) If set, the user will be redirected to this URL
74 * @return bool 74 * @return bool
75 * 75 *
76 */ 76 */
77 public function add($type, $message, $redirect_to=null) { 77 public function add($type, $message, $redirect_to=null) {
78 78
79 if( !isset($_SESSION['flash_messages']) ) return false; 79 if( !isset($_SESSION['flash_messages']) ) return false;
80 80
81 if( !isset($type) || !isset($message[0]) ) return false; 81 if( !isset($type) || !isset($message[0]) ) return false;
82 82
83 // Replace any shorthand codes with their full version 83 // Replace any shorthand codes with their full version
84 if( strlen(trim($type)) == 1 ) { 84 if( strlen(trim($type)) == 1 ) {
85 $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); 85 $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
86 86
87 // Backwards compatibility... 87 // Backwards compatibility...
88 } elseif( $type == 'information' ) { 88 } elseif( $type == 'information' ) {
89 $type = 'info'; 89 $type = 'info';
90 } 90 }
91 91
92 // Make sure it's a valid message type 92 // Make sure it's a valid message type
93 if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); 93 if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
94 94
95 // If the session array doesn't exist, create it 95 // If the session array doesn't exist, create it
96 if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); 96 if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
97 97
98 $_SESSION['flash_messages'][$type][] = $message; 98 $_SESSION['flash_messages'][$type][] = $message;
99 99
100 if( !is_null($redirect_to) ) { 100 if( !is_null($redirect_to) ) {
101 header("Location: $redirect_to"); 101 header("Location: $redirect_to");
102 exit(); 102 exit();
103 } 103 }
104 104
105 return true; 105 return true;
106 106
107 } 107 }
108 108
109 //----------------------------------------------------------------------------------------------- 109 //-----------------------------------------------------------------------------------------------
110 // display() 110 // display()
111 // print queued messages to the screen 111 // print queued messages to the screen
112 //----------------------------------------------------------------------------------------------- 112 //-----------------------------------------------------------------------------------------------
113 /** 113 /**
114 * Display the queued messages 114 * Display the queued messages
115 * 115 *
116 * @author Mike Everhart 116 * @author Mike Everhart
117 * 117 *
118 * @param string $type Which messages to display 118 * @param string $type Which messages to display
119 * @param bool $print True = print the messages on the screen 119 * @param bool $print True = print the messages on the screen
120 * @return mixed 120 * @return mixed
121 * 121 *
122 */ 122 */
123 public function display($type='all', $print=true) { 123 public function display($type='all', $print=true) {
124 $messages = ''; 124 $messages = '';
125 $data = ''; 125 $data = '';
126 126
127 if( !isset($_SESSION['flash_messages']) ) return false; 127 if( !isset($_SESSION['flash_messages']) ) return false;
128 128
129 if( $type == 'g' || $type == 'growl' ) { 129 if( $type == 'g' || $type == 'growl' ) {
130 $this->displayGrowlMessages(); 130 $this->displayGrowlMessages();
131 return true; 131 return true;
132 } 132 }
133 133
134 // Print a certain type of message? 134 // Print a certain type of message?
135 if( in_array($type, $this->msgTypes) ) { 135 if( in_array($type, $this->msgTypes) ) {
136 foreach( $_SESSION['flash_messages'][$type] as $msg ) { 136 foreach( $_SESSION['flash_messages'][$type] as $msg ) {
137 $messages .= $this->msgBefore . $msg . $this->msgAfter; 137 $messages .= $this->msgBefore . $msg . $this->msgAfter;
138 } 138 }
139 139
140 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); 140 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
141 141
142 // Clear the viewed messages 142 // Clear the viewed messages
143 $this->clear($type); 143 $this->clear($type);
144 144
145 // Print ALL queued messages 145 // Print ALL queued messages
146 } elseif( $type == 'all' ) { 146 } elseif( $type == 'all' ) {
147 foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { 147 foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
148 $messages = ''; 148 $messages = '';
149 foreach( $msgArray as $msg ) { 149 foreach( $msgArray as $msg ) {
150 $messages .= $this->msgBefore . $msg . $this->msgAfter; 150 $messages .= $this->msgBefore . $msg . $this->msgAfter;
151 } 151 }
152 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); 152 $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
153 } 153 }
154 154
155 // Clear ALL of the messages 155 // Clear ALL of the messages
156 $this->clear(); 156 $this->clear();
157 157
158 // Invalid Message Type? 158 // Invalid Message Type?
159 } else { 159 } else {
160 return false; 160 return false;
161 } 161 }
162 162
163 // Print everything to the screen or return the data 163 // Print everything to the screen or return the data
164 if( $print ) { 164 if( $print ) {
165 echo $data; 165 echo $data;
166 } else { 166 } else {
167 return $data; 167 return $data;
168 } 168 }
169 } 169 }
170 170
171 171
172 /** 172 /**
173 * Check to see if there are any queued error messages 173 * Check to see if there are any queued error messages
174 * 174 *
175 * @author Mike Everhart 175 * @author Mike Everhart
176 * 176 *
177 * @return bool true = There ARE error messages 177 * @return bool true = There ARE error messages
178 * false = There are NOT any error messages 178 * false = There are NOT any error messages
179 * 179 *
180 */ 180 */
181 public function hasErrors() { 181 public function hasErrors() {
182 return empty($_SESSION['flash_messages']['error']) ? false : true; 182 return empty($_SESSION['flash_messages']['error']) ? false : true;
183 } 183 }
184 184
185 /** 185 /**
186 * Check to see if there are any ($type) messages queued 186 * Check to see if there are any ($type) messages queued
187 * 187 *
188 * @author Mike Everhart 188 * @author Mike Everhart
189 * 189 *
190 * @param string $type The type of messages to check for 190 * @param string $type The type of messages to check for
191 * @return bool 191 * @return bool
192 * 192 *
193 */ 193 */
194 public function hasMessages($type=null) { 194 public function hasMessages($type=null) {
195 if( !is_null($type) ) { 195 if( !is_null($type) ) {
196 if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; 196 if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];
197 } else { 197 } else {
198 foreach( $this->msgTypes as $type ) { 198 foreach( $this->msgTypes as $type ) {
199 if( !empty($_SESSION['flash_messages']) ) return true; 199 if( !empty($_SESSION['flash_messages']) ) return true;
200 } 200 }
201 } 201 }
202 return false; 202 return false;
203 } 203 }
204 204
205 /** 205 /**
206 * Clear messages from the session data 206 * Clear messages from the session data
207 * 207 *
208 * @author Mike Everhart 208 * @author Mike Everhart
209 * 209 *
210 * @param string $type The type of messages to clear 210 * @param string $type The type of messages to clear
211 * @return bool 211 * @return bool
212 * 212 *
213 */ 213 */
214 public function clear($type='all') { 214 public function clear($type='all') {
215 if( $type == 'all' ) { 215 if( $type == 'all' ) {
216 unset($_SESSION['flash_messages']); 216 unset($_SESSION['flash_messages']);
217 } else { 217 } else {
218 unset($_SESSION['flash_messages'][$type]); 218 unset($_SESSION['flash_messages'][$type]);
219 } 219 }
220 return true; 220 return true;
221 } 221 }
222 222
223 public function __toString() { return $this->hasMessages(); } 223 public function __toString() { return $this->hasMessages(); }
224 224
225 public function __destruct() { 225 public function __destruct() {
226 //$this->clear(); 226 //$this->clear();
227 } 227 }
228 228
229 229
230} // end class 230} // end class
231?> \ No newline at end of file 231?> \ No newline at end of file
diff --git a/inc/3rdparty/paginator.php b/inc/3rdparty/paginator.php
new file mode 100644
index 00000000..306756c0
--- /dev/null
+++ b/inc/3rdparty/paginator.php
@@ -0,0 +1,202 @@
1<?php
2/*
3 * PHP Pagination Class
4 *
5 * @author David Carr - dave@daveismyname.com - http://www.daveismyname.com
6 * @version 1.0
7 * @date October 20, 2013
8 */
9class Paginator{
10
11 /**
12 * set the number of items per page.
13 *
14 * @var numeric
15 */
16 private $_perPage;
17
18 /**
19 * set get parameter for fetching the page number
20 *
21 * @var string
22 */
23 private $_instance;
24
25 /**
26 * sets the page number.
27 *
28 * @var numeric
29 */
30 private $_page;
31
32 /**
33 * set the limit for the data source
34 *
35 * @var string
36 */
37 private $_limit;
38
39 /**
40 * set the total number of records/items.
41 *
42 * @var numeric
43 */
44 private $_totalRows = 0;
45
46
47
48 /**
49 * __construct
50 *
51 * pass values when class is istantiated
52 *
53 * @param numeric $_perPage sets the number of iteems per page
54 * @param numeric $_instance sets the instance for the GET parameter
55 */
56 public function __construct($perPage,$instance){
57 $this->_instance = $instance;
58 $this->_perPage = $perPage;
59 $this->set_instance();
60 }
61
62 /**
63 * get_start
64 *
65 * creates the starting point for limiting the dataset
66 * @return numeric
67 */
68 private function get_start(){
69 return ($this->_page * $this->_perPage) - $this->_perPage;
70 }
71
72 /**
73 * set_instance
74 *
75 * sets the instance parameter, if numeric value is 0 then set to 1
76 *
77 * @var numeric
78 */
79 private function set_instance(){
80 $this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]);
81 $this->_page = ($this->_page == 0 ? 1 : $this->_page);
82 }
83
84 /**
85 * set_total
86 *
87 * collect a numberic value and assigns it to the totalRows
88 *
89 * @var numeric
90 */
91 public function set_total($_totalRows){
92 $this->_totalRows = $_totalRows;
93 }
94
95 /**
96 * get_limit
97 *
98 * returns the limit for the data source, calling the get_start method and passing in the number of items perp page
99 *
100 * @return string
101 */
102 public function get_limit(){
103 if (STORAGE == 'postgres') {
104 return "LIMIT ".$this->_perPage." OFFSET ".$this->get_start();
105 } else {
106 return "LIMIT ".$this->get_start().",".$this->_perPage;
107 }
108 }
109
110 /**
111 * page_links
112 *
113 * create the html links for navigating through the dataset
114 *
115 * @var sting $path optionally set the path for the link
116 * @var sting $ext optionally pass in extra parameters to the GET
117 * @return string returns the html menu
118 */
119 public function page_links($path='?',$ext=null)
120 {
121 $adjacents = "2";
122 $prev = $this->_page - 1;
123 $next = $this->_page + 1;
124 $lastpage = ceil($this->_totalRows/$this->_perPage);
125 $lpm1 = $lastpage - 1;
126
127 $pagination = "";
128 if($lastpage > 1)
129 {
130 $pagination .= "<div class='pagination'>";
131 if ($this->_page > 1)
132 $pagination.= "<a href='".$path."$this->_instance=$prev"."$ext'>« previous</a>";
133 else
134 $pagination.= "<span class='disabled'>« previous</span>";
135
136 if ($lastpage < 7 + ($adjacents * 2))
137 {
138 for ($counter = 1; $counter <= $lastpage; $counter++)
139 {
140 if ($counter == $this->_page)
141 $pagination.= "<span class='current'>$counter</span>";
142 else
143 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
144 }
145 }
146 elseif($lastpage > 5 + ($adjacents * 2))
147 {
148 if($this->_page < 1 + ($adjacents * 2))
149 {
150 for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
151 {
152 if ($counter == $this->_page)
153 $pagination.= "<span class='current'>$counter</span>";
154 else
155 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
156 }
157 $pagination.= "...";
158 $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
159 $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
160 }
161 elseif($lastpage - ($adjacents * 2) > $this->_page && $this->_page > ($adjacents * 2))
162 {
163 $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
164 $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
165 $pagination.= "...";
166 for ($counter = $this->_page - $adjacents; $counter <= $this->_page + $adjacents; $counter++)
167 {
168 if ($counter == $this->_page)
169 $pagination.= "<span class='current'>$counter</span>";
170 else
171 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
172 }
173 $pagination.= "..";
174 $pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
175 $pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
176 }
177 else
178 {
179 $pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
180 $pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
181 $pagination.= "..";
182 for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++)
183 {
184 if ($counter == $this->_page)
185 $pagination.= "<span class='current'>$counter</span>";
186 else
187 $pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
188 }
189 }
190 }
191
192 if ($this->_page < $counter - 1)
193 $pagination.= "<a href='".$path."$this->_instance=$next"."$ext'>next »</a>";
194 else
195 $pagination.= "<span class='disabled'>next »</span>";
196 $pagination.= "</div>\n";
197 }
198
199
200 return $pagination;
201 }
202}
diff --git a/inc/simple_html_dom.php b/inc/3rdparty/simple_html_dom.php
index 43b94e57..43b94e57 100644
--- a/inc/simple_html_dom.php
+++ b/inc/3rdparty/simple_html_dom.php
diff --git a/inc/MyTool.class.php b/inc/MyTool.class.php
deleted file mode 100644
index b11f18e9..00000000
--- a/inc/MyTool.class.php
+++ /dev/null
@@ -1,265 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class MyTool
12{
13 public static function initPhp()
14 {
15 define('START_TIME', microtime(true));
16
17 if (phpversion() < 5) {
18 die(_('Oops, it seems you don\'t have PHP 5.'));
19 }
20
21 error_reporting(E_ALL);
22
23 function stripslashesDeep($value) {
24 return is_array($value)
25 ? array_map('stripslashesDeep', $value)
26 : stripslashes($value);
27 }
28
29 if (get_magic_quotes_gpc()) {
30 $_POST = array_map('stripslashesDeep', $_POST);
31 $_GET = array_map('stripslashesDeep', $_GET);
32 $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
33 }
34
35 ob_start();
36 register_shutdown_function('ob_end_flush');
37 }
38
39 public static function isUrl($url)
40 {
41 // http://neo22s.com/check-if-url-exists-and-is-online-php/
42 $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
43
44 return preg_match($pattern, $url);
45 }
46
47 public static function isEmail($email)
48 {
49 $pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i";
50
51 return (preg_match($pattern, $email));
52 }
53
54 public static function formatBBCode($text)
55 {
56 $replace = array(
57 '/\[m\](.+?)\[\/m\]/is'
58 => '/* moderate */',
59 '/\[b\](.+?)\[\/b\]/is'
60 => '<strong>$1</strong>',
61 '/\[i\](.+?)\[\/i\]/is'
62 => '<em>$1</em>',
63 '/\[s\](.+?)\[\/s\]/is'
64 => '<del>$1</del>',
65 '/\[u\](.+?)\[\/u\]/is'
66 => '<span style="text-decoration: underline;">$1</span>',
67 '/\[url\](.+?)\[\/url]/is'
68 => '<a href="$1">$1</a>',
69 '/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
70 => '<a href="$1">$2</a>',
71 '/\[quote\](.+?)\[\/quote\]/is'
72 => '<blockquote>$1</blockquote>',
73 '/\[code\](.+?)\[\/code\]/is'
74 => '<code>$1</code>',
75 '/\[([^[]+)\|([^[]+)\]/is'
76 => '<a href="$2">$1</a>'
77 );
78 $text = preg_replace(
79 array_keys($replace),
80 array_values($replace),
81 $text
82 );
83
84 return $text;
85 }
86
87 public static function formatText($text)
88 {
89 $text = preg_replace_callback(
90 '/<code_html>(.*?)<\/code_html>/is',
91 create_function(
92 '$matches',
93 'return htmlspecialchars($matches[1]);'
94 ),
95 $text
96 );
97 $text = preg_replace_callback(
98 '/<code_php>(.*?)<\/code_php>/is',
99 create_function(
100 '$matches',
101 'return highlight_string("<?php $matches[1] ?>", true);'
102 ),
103 $text
104 );
105 $text = preg_replace('/<br \/>/is', '', $text);
106
107 $text = preg_replace(
108 '#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
109 '\\1<a href="\\2">\\2</a>\\4',
110 $text
111 );
112 $text = preg_replace(
113 '#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
114 '\\1<a href="http://\\2.wikipedia.org/wiki/\\3">\\3</a>',
115 $text
116 );
117 $text = str_replace(
118 'http://.wikipedia.org/wiki/',
119 'http://www.wikipedia.org/wiki/',
120 $text
121 );
122 $text = str_replace('\wp:', 'wp:', $text);
123 $text = str_replace('\http:', 'http:', $text);
124 $text = MyTool::formatBBCode($text);
125 $text = nl2br($text);
126
127 return $text;
128 }
129
130 public static function getUrl()
131 {
132 $https = (!empty($_SERVER['HTTPS'])
133 && (strtolower($_SERVER['HTTPS']) == 'on'))
134 || (isset($_SERVER["SERVER_PORT"])
135 && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
136 $serverport = (!isset($_SERVER["SERVER_PORT"])
137 || $_SERVER["SERVER_PORT"] == '80'
138 || ($https && $_SERVER["SERVER_PORT"] == '443')
139 ? ''
140 : ':' . $_SERVER["SERVER_PORT"]);
141
142 $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
143
144 if (!isset($_SERVER["SERVER_NAME"])) {
145 return $scriptname;
146 }
147
148 return 'http' . ($https ? 's' : '') . '://'
149 . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
150 }
151
152 public static function rrmdir($dir)
153 {
154 if (is_dir($dir) && ($d = @opendir($dir))) {
155 while (($file = @readdir($d)) !== false) {
156 if ( $file == '.' || $file == '..' ) {
157 continue;
158 } else {
159 unlink($dir . '/' . $file);
160 }
161 }
162 }
163 }
164
165 public static function humanBytes($bytes)
166 {
167 $siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
168 $base = 1024;
169 $class = min((int) log($bytes, $base), count($siPrefix) - 1);
170 $val = sprintf('%1.2f', $bytes / pow($base, $class));
171
172 return $val . ' ' . $siPrefix[$class];
173 }
174
175 public static function returnBytes($val)
176 {
177 $val = trim($val);
178 $last = strtolower($val[strlen($val)-1]);
179 switch($last)
180 {
181 case 'g': $val *= 1024;
182 case 'm': $val *= 1024;
183 case 'k': $val *= 1024;
184 }
185
186 return $val;
187 }
188
189 public static function getMaxFileSize()
190 {
191 $sizePostMax = MyTool::returnBytes(ini_get('post_max_size'));
192 $sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize'));
193
194 // Return the smaller of two:
195 return min($sizePostMax, $sizeUploadMax);
196 }
197
198 public static function smallHash($text)
199 {
200 $t = rtrim(base64_encode(hash('crc32', $text, true)), '=');
201 // Get rid of characters which need encoding in URLs.
202 $t = str_replace('+', '-', $t);
203 $t = str_replace('/', '_', $t);
204 $t = str_replace('=', '@', $t);
205
206 return $t;
207 }
208
209 public static function renderJson($data)
210 {
211 header('Cache-Control: no-cache, must-revalidate');
212 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
213 header('Content-type: application/json; charset=UTF-8');
214
215 echo json_encode($data);
216 exit();
217 }
218
219 public static function grabToLocal($url, $file, $force = false)
220 {
221 if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){
222 $ch = curl_init ($url);
223 curl_setopt($ch, CURLOPT_HEADER, false);
224 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
225 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
226 $raw = curl_exec($ch);
227 if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
228 $fp = fopen($file, 'x');
229 fwrite($fp, $raw);
230 fclose($fp);
231 }
232 curl_close ($ch);
233 }
234 }
235
236 public static function redirect($rurl = '')
237 {
238 if ($rurl === '') {
239 // if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
240 $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
241 if (isset($_POST['returnurl'])) {
242 $rurl = $_POST['returnurl'];
243 }
244 }
245
246 // prevent loop
247 if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
248 $rurl = MyTool::getUrl();
249 }
250
251 if (substr($rurl, 0, 1) !== '?') {
252 $ref = MyTool::getUrl();
253 if (substr($rurl, 0, strlen($ref)) !== $ref) {
254 $rurl = $ref;
255 }
256 }
257 header('Location: '.$rurl);
258 exit();
259 }
260
261 public static function silence_errors($num, $str)
262 {
263// No-op
264 }
265} \ No newline at end of file
diff --git a/inc/config.php b/inc/config.php
deleted file mode 100644
index 6e3f80b8..00000000
--- a/inc/config.php
+++ /dev/null
@@ -1,73 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <nicolas@loeuillet.org>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11define ('POCHE_VERSION', '0.3');
12
13if (!is_dir('db/')) {
14 @mkdir('db/',0705);
15}
16
17define ('MODE_DEMO', FALSE);
18define ('ABS_PATH', 'assets/');
19define ('CONVERT_LINKS_FOOTNOTES', TRUE);
20define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE);
21define ('DOWNLOAD_PICTURES', TRUE);
22define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
23define ('LANG', 'fr_FR.UTF8');
24
25putenv("LC_ALL=".LANG);
26setlocale(LC_ALL, LANG);
27bindtextdomain(LANG, "./locale");
28textdomain(LANG);
29
30$storage_type = 'sqlite'; # sqlite or file
31
32include 'functions.php';
33require_once 'Readability.php';
34require_once 'Encoding.php';
35require_once 'rain.tpl.class.php';
36require_once 'MyTool.class.php';
37require_once 'Session.class.php';
38require_once 'store/store.class.php';
39require_once 'store/sqlite.class.php';
40require_once 'store/file.class.php';
41require_once 'class.messages.php';
42
43Session::init();
44
45$store = new $storage_type();
46# initialisation de RainTPL
47raintpl::$tpl_dir = './tpl/';
48raintpl::$cache_dir = './cache/';
49raintpl::$base_url = get_poche_url();
50raintpl::configure('path_replace', false);
51raintpl::configure('debug', false);
52$tpl = new raintpl();
53
54if(!$store->isInstalled())
55{
56 logm('poche still not installed');
57 $tpl->draw('install');
58 if (isset($_GET['install'])) {
59 if (($_POST['password'] == $_POST['password_repeat'])
60 && $_POST['password'] != "" && $_POST['login'] != "") {
61 $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login']));
62 Session::logout();
63 MyTool::redirect();
64 }
65 }
66 exit();
67}
68
69$_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin();
70$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword();
71
72$msg = new Messages();
73$tpl->assign('msg', $msg); \ No newline at end of file
diff --git a/inc/functions.php b/inc/functions.php
deleted file mode 100644
index ee26fbaa..00000000
--- a/inc/functions.php
+++ /dev/null
@@ -1,400 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11/**
12 * Permet de générer l'URL de poche pour le bookmarklet
13 */
14function get_poche_url()
15{
16 $protocol = "http";
17 if(isset($_SERVER['HTTPS'])) {
18 if($_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "") {
19 $protocol = "https";
20 }
21 }
22
23 return $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
24}
25
26function encode_string($string)
27{
28 return sha1($string . SALT);
29}
30
31// function define to retrieve url content
32function get_external_file($url)
33{
34 $timeout = 15;
35 // spoofing FireFox 18.0
36 $useragent="Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
37
38 if (in_array ('curl', get_loaded_extensions())) {
39 // Fetch feed from URL
40 $curl = curl_init();
41 curl_setopt($curl, CURLOPT_URL, $url);
42 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
43 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
44 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
45 curl_setopt($curl, CURLOPT_HEADER, false);
46
47 // FOR SSL do not verified certificate
48 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
49 curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
50
51 // FeedBurner requires a proper USER-AGENT...
52 curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
53 curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
54 curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
55
56 $data = curl_exec($curl);
57
58 $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
59
60 $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
61
62 curl_close($curl);
63 } else {
64
65 // create http context and add timeout and user-agent
66 $context = stream_context_create(array(
67 'http'=>array('timeout' => $timeout,
68 'header'=> "User-Agent: ".$useragent, /*spoot Mozilla Firefox*/
69 'follow_location' => true),
70 // FOR SSL do not verified certificate
71 'ssl' => array('verify_peer' => false,
72 'allow_self_signed' => true)
73 )
74 );
75
76 // only download page lesser than 4MB
77 $data = @file_get_contents($url, false, $context, -1, 4000000); // We download at most 4 MB from source.
78
79 if(isset($http_response_header) and isset($http_response_header[0])) {
80 $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE));
81 }
82 }
83
84 // if response is not empty and response is OK
85 if (isset($data) and isset($httpcodeOK) and $httpcodeOK ) {
86
87 // take charset of page and get it
88 preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
89
90 // if meta tag is found
91 if (!empty($meta[0])) {
92 // retrieve encoding in $enc
93 preg_match('#charset="?(.*)"#si', $meta[0], $enc);
94
95 // if charset is found set it otherwise, set it to utf-8
96 $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
97
98 } else {
99 $html_charset = 'utf-8';
100 $enc[1] = '';
101 }
102
103 // replace charset of url to charset of page
104 $data = str_replace('charset='.$enc[1], 'charset='.$html_charset, $data);
105
106 return $data;
107 }
108 else {
109 return FALSE;
110 }
111}
112
113/**
114 * Préparation de l'URL avec récupération du contenu avant insertion en base
115 */
116function prepare_url($url)
117{
118 $parametres = array();
119 $url = html_entity_decode(trim($url));
120
121 // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
122 // from shaarli, by sebsauvage
123 $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i);
124 $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i);
125 $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i);
126
127 $title = $url;
128 $html = Encoding::toUTF8(get_external_file($url,15));
129 // If get_external_file if not able to retrieve HTTPS content try the same URL with HTTP protocol
130 if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) {
131 $url = 'http://' . $url;
132 $html = Encoding::toUTF8(get_external_file($url,15));
133 }
134
135 if (function_exists('tidy_parse_string')) {
136 $tidy = tidy_parse_string($html, array(), 'UTF8');
137 $tidy->cleanRepair();
138 $html = $tidy->value;
139 }
140
141 if (isset($html) and strlen($html) > 0)
142 {
143 $r = new Readability($html, $url);
144
145 $r->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
146 $r->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
147
148 if($r->init())
149 {
150 $content = $r->articleContent->innerHTML;
151 $parametres['title'] = $r->articleTitle->innerHTML;
152 $parametres['content'] = $content;
153 return $parametres;
154 }
155 }
156
157 return FALSE;
158}
159
160/**
161 * On modifie les URLS des images dans le corps de l'article
162 */
163function filtre_picture($content, $url, $id)
164{
165 $matches = array();
166 preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
167 foreach($matches as $i => $link)
168 {
169 $link[1] = trim($link[1]);
170 if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) )
171 {
172 $absolute_path = get_absolute_link($link[2],$url);
173 $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
174 $directory = create_assets_directory($id);
175 $fullpath = $directory . '/' . $filename;
176 download_pictures($absolute_path, $fullpath);
177 $content = str_replace($matches[$i][2], $fullpath, $content);
178 }
179
180 }
181
182 return $content;
183}
184
185/**
186 * Retourne le lien absolu
187 */
188function get_absolute_link($relative_link, $url)
189{
190 /* return if already absolute URL */
191 if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
192
193 /* queries and anchors */
194 if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
195
196 /* parse base URL and convert to local variables:
197 $scheme, $host, $path */
198 extract(parse_url($url));
199
200 /* remove non-directory element from path */
201 $path = preg_replace('#/[^/]*$#', '', $path);
202
203 /* destroy path if relative url points to root */
204 if ($relative_link[0] == '/') $path = '';
205
206 /* dirty absolute URL */
207 $abs = $host . $path . '/' . $relative_link;
208
209 /* replace '//' or '/./' or '/foo/../' with '/' */
210 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
211 for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
212
213 /* absolute URL is ready! */
214 return $scheme.'://'.$abs;
215}
216
217/**
218 * Téléchargement des images
219 */
220
221function download_pictures($absolute_path, $fullpath)
222{
223 $rawdata = get_external_file($absolute_path);
224
225 if(file_exists($fullpath)) {
226 unlink($fullpath);
227 }
228 $fp = fopen($fullpath, 'x');
229 fwrite($fp, $rawdata);
230 fclose($fp);
231}
232
233/**
234 * Crée un répertoire de médias pour l'article
235 */
236function create_assets_directory($id)
237{
238 $assets_path = ABS_PATH;
239 if(!is_dir($assets_path)) {
240 mkdir($assets_path, 0705);
241 }
242
243 $article_directory = $assets_path . $id;
244 if(!is_dir($article_directory)) {
245 mkdir($article_directory, 0705);
246 }
247
248 return $article_directory;
249}
250
251/**
252 * Suppression du répertoire d'images
253 */
254function remove_directory($directory)
255{
256 if(is_dir($directory)) {
257 $files = array_diff(scandir($directory), array('.','..'));
258 foreach ($files as $file) {
259 (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
260 }
261 return rmdir($directory);
262 }
263}
264
265function display_view($view, $id = 0, $full_head = 'yes')
266{
267 global $tpl, $store, $msg;
268
269 switch ($view)
270 {
271 case 'export':
272 $entries = $store->retrieveAll();
273 $tpl->assign('export', myTool::renderJson($entries));
274 $tpl->draw('export');
275 logm('export view');
276 break;
277 case 'config':
278 $tpl->assign('load_all_js', 0);
279 $tpl->draw('head');
280 $tpl->draw('home');
281 $tpl->draw('config');
282 $tpl->draw('js');
283 $tpl->draw('footer');
284 logm('config view');
285 break;
286 case 'view':
287 $entry = $store->retrieveOneById($id);
288
289 if ($entry != NULL) {
290 $tpl->assign('id', $entry['id']);
291 $tpl->assign('url', $entry['url']);
292 $tpl->assign('title', $entry['title']);
293 $content = $entry['content'];
294 if (function_exists('tidy_parse_string')) {
295 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
296 $tidy->cleanRepair();
297 $content = $tidy->value;
298 }
299 $tpl->assign('content', $content);
300 $tpl->assign('is_fav', $entry['is_fav']);
301 $tpl->assign('is_read', $entry['is_read']);
302 $tpl->assign('load_all_js', 0);
303 $tpl->draw('view');
304 }
305 else {
306 logm('error in view call : entry is NULL');
307 }
308
309 logm('view link #' . $id);
310 break;
311 default: # home view
312 $entries = $store->getEntriesByView($view);
313
314 $tpl->assign('entries', $entries);
315
316 if ($full_head == 'yes') {
317 $tpl->assign('load_all_js', 1);
318 $tpl->draw('head');
319 $tpl->draw('home');
320 }
321
322 $tpl->draw('entries');
323
324 if ($full_head == 'yes') {
325 $tpl->draw('js');
326 $tpl->draw('footer');
327 }
328 break;
329 }
330}
331
332/**
333 * Appel d'une action (mark as fav, archive, delete)
334 */
335function action_to_do($action, $url, $id = 0)
336{
337 global $store, $msg;
338
339 switch ($action)
340 {
341 case 'add':
342 if ($url == '')
343 continue;
344
345 $url = base64_decode($url);
346 error_log(print_r($url, TRUE));
347 if (MyTool::isUrl($url)) {
348 if($parametres_url = prepare_url($url)) {
349 if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) {
350 $last_id = $store->getLastId();
351 if (DOWNLOAD_PICTURES) {
352 $content = filtre_picture($parametres_url['content'], $url, $last_id);
353 }
354 $msg->add('s', _('the link has been added successfully'));
355 }
356 else {
357 $msg->add('e', _('error during insertion : the link wasn\'t added'));
358 }
359 }
360 else {
361 $msg->add('e', _('error during url preparation : the link wasn\'t added'));
362 logm('error during url preparation');
363 }
364 }
365 else {
366 $msg->add('e', _('error during url preparation : the link is not valid'));
367 logm($url . ' is not a valid url');
368 }
369
370 logm('add link ' . $url);
371 break;
372 case 'delete':
373 if ($store->deleteById($id)) {
374 remove_directory(ABS_PATH . $id);
375 $msg->add('s', _('the link has been deleted successfully'));
376 logm('delete link #' . $id);
377 }
378 else {
379 $msg->add('e', _('the link wasn\'t deleted'));
380 logm('error : can\'t delete link #' . $id);
381 }
382 break;
383 case 'toggle_fav' :
384 $store->favoriteById($id);
385 logm('mark as favorite link #' . $id);
386 break;
387 case 'toggle_archive' :
388 $store->archiveById($id);
389 logm('archive link #' . $id);
390 break;
391 default:
392 break;
393 }
394}
395
396function logm($message)
397{
398 $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n";
399 file_put_contents('./log.txt',$t,FILE_APPEND);
400}
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php
new file mode 100644
index 00000000..cd5a9a31
--- /dev/null
+++ b/inc/poche/Database.class.php
@@ -0,0 +1,216 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Database {
12 var $handle;
13
14 function __construct()
15 {
16 switch (STORAGE) {
17 case 'sqlite':
18 $db_path = 'sqlite:' . STORAGE_SQLITE;
19 $this->handle = new PDO($db_path);
20 break;
21 case 'mysql':
22 $db_path = 'mysql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
23 $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD);
24 break;
25 case 'postgres':
26 $db_path = 'pgsql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB;
27 $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD);
28 break;
29 }
30
31 $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
32 Tools::logm('storage type ' . STORAGE);
33 }
34
35 private function getHandle() {
36 return $this->handle;
37 }
38
39 public function isInstalled() {
40 $sql = "SELECT username FROM users";
41 $query = $this->executeQuery($sql, array());
42 $hasAdmin = count($query->fetchAll());
43
44 if ($hasAdmin == 0)
45 return FALSE;
46
47 return TRUE;
48 }
49
50 public function install($login, $password) {
51 $sql = 'INSERT INTO users ( username, password, name, email) VALUES (?, ?, ?, ?)';
52 $params = array($login, $password, $login, ' ');
53 $query = $this->executeQuery($sql, $params);
54
55 $sequence = '';
56 if (STORAGE == 'postgres') {
57 $sequence = 'users_id_seq';
58 }
59
60 $id_user = intval($this->getLastId($sequence));
61
62 $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
63 $params = array($id_user, 'pager', '10');
64 $query = $this->executeQuery($sql, $params);
65
66 $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)';
67 $params = array($id_user, 'language', 'en_EN.UTF8');
68 $query = $this->executeQuery($sql, $params);
69
70 return TRUE;
71 }
72
73 private function getConfigUser($id) {
74 $sql = "SELECT * FROM users_config WHERE user_id = ?";
75 $query = $this->executeQuery($sql, array($id));
76 $result = $query->fetchAll();
77 $user_config = array();
78
79 foreach ($result as $key => $value) {
80 $user_config[$value['name']] = $value['value'];
81 }
82
83 return $user_config;
84 }
85
86 public function login($username, $password) {
87 $sql = "SELECT * FROM users WHERE username=? AND password=?";
88 $query = $this->executeQuery($sql, array($username, $password));
89 $login = $query->fetchAll();
90
91 $user = array();
92 if (isset($login[0])) {
93 $user['id'] = $login[0]['id'];
94 $user['username'] = $login[0]['username'];
95 $user['password'] = $login[0]['password'];
96 $user['name'] = $login[0]['name'];
97 $user['email'] = $login[0]['email'];
98 $user['config'] = $this->getConfigUser($login[0]['id']);
99 }
100
101 return $user;
102 }
103
104 public function updatePassword($id, $password)
105 {
106 $sql_update = "UPDATE users SET password=? WHERE id=?";
107 $params_update = array($password, $id);
108 $query = $this->executeQuery($sql_update, $params_update);
109 }
110
111 private function executeQuery($sql, $params) {
112 try
113 {
114 $query = $this->getHandle()->prepare($sql);
115 $query->execute($params);
116 return $query;
117 }
118 catch (Exception $e)
119 {
120 Tools::logm('execute query error : '.$e->getMessage());
121 return FALSE;
122 }
123 }
124
125 public function retrieveAll($user_id) {
126 $sql = "SELECT * FROM entries WHERE user_id=? ORDER BY id";
127 $query = $this->executeQuery($sql, array($user_id));
128 $entries = $query->fetchAll();
129
130 return $entries;
131 }
132
133 public function retrieveOneById($id, $user_id) {
134 $entry = NULL;
135 $sql = "SELECT * FROM entries WHERE id=? AND user_id=?";
136 $params = array(intval($id), $user_id);
137 $query = $this->executeQuery($sql, $params);
138 $entry = $query->fetchAll();
139
140 return $entry[0];
141 }
142
143 public function getEntriesByView($view, $user_id, $limit = '') {
144 switch ($_SESSION['sort'])
145 {
146 case 'ia':
147 $order = 'ORDER BY id';
148 break;
149 case 'id':
150 $order = 'ORDER BY id DESC';
151 break;
152 case 'ta':
153 $order = 'ORDER BY lower(title)';
154 break;
155 case 'td':
156 $order = 'ORDER BY lower(title) DESC';
157 break;
158 default:
159 $order = 'ORDER BY id';
160 break;
161 }
162
163 switch ($view)
164 {
165 case 'archive':
166 $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
167 $params = array($user_id, 1);
168 break;
169 case 'fav' :
170 $sql = "SELECT * FROM entries WHERE user_id=? AND is_fav=? " . $order;
171 $params = array($user_id, 1);
172 break;
173 default:
174 $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order;
175 $params = array($user_id, 0);
176 break;
177 }
178
179 $sql .= ' ' . $limit;
180
181 $query = $this->executeQuery($sql, $params);
182 $entries = $query->fetchAll();
183
184 return $entries;
185 }
186
187 public function add($url, $title, $content, $user_id) {
188 $sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)';
189 $params_action = array($url, $title, $content, $user_id);
190 $query = $this->executeQuery($sql_action, $params_action);
191 return $query;
192 }
193
194 public function deleteById($id, $user_id) {
195 $sql_action = "DELETE FROM entries WHERE id=? AND user_id=?";
196 $params_action = array($id, $user_id);
197 $query = $this->executeQuery($sql_action, $params_action);
198 return $query;
199 }
200
201 public function favoriteById($id, $user_id) {
202 $sql_action = "UPDATE entries SET is_fav=NOT is_fav WHERE id=? AND user_id=?";
203 $params_action = array($id, $user_id);
204 $query = $this->executeQuery($sql_action, $params_action);
205 }
206
207 public function archiveById($id, $user_id) {
208 $sql_action = "UPDATE entries SET is_read=NOT is_read WHERE id=? AND user_id=?";
209 $params_action = array($id, $user_id);
210 $query = $this->executeQuery($sql_action, $params_action);
211 }
212
213 public function getLastId($column = '') {
214 return $this->getHandle()->lastInsertId($column);
215 }
216}
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
new file mode 100644
index 00000000..38b4a98e
--- /dev/null
+++ b/inc/poche/Poche.class.php
@@ -0,0 +1,429 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Poche
12{
13 public $user;
14 public $store;
15 public $tpl;
16 public $messages;
17 public $pagination;
18
19 function __construct()
20 {
21 $this->store = new Database();
22 $this->init();
23 $this->messages = new Messages();
24
25 # installation
26 if(!$this->store->isInstalled())
27 {
28 $this->install();
29 }
30 }
31
32 private function init()
33 {
34 Tools::initPhp();
35 Session::init();
36
37 if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
38 $this->user = $_SESSION['poche_user'];
39 }
40 else {
41 # fake user, just for install & login screens
42 $this->user = new User();
43 $this->user->setConfig($this->getDefaultConfig());
44 }
45
46 # l10n
47 $language = $this->user->getConfigValue('language');
48 putenv('LC_ALL=' . $language);
49 setlocale(LC_ALL, $language);
50 bindtextdomain($language, LOCALE);
51 textdomain($language);
52
53 # template engine
54 $loader = new Twig_Loader_Filesystem(TPL);
55 if (DEBUG_POCHE) {
56 $twig_params = array();
57 }
58 else {
59 $twig_params = array('cache' => CACHE);
60 }
61 $this->tpl = new Twig_Environment($loader, $twig_params);
62 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
63 # filter to display domain name of an url
64 $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
65 $this->tpl->addFilter($filter);
66
67 # Pagination
68 $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
69 }
70
71 private function install()
72 {
73 Tools::logm('poche still not installed');
74 echo $this->tpl->render('install.twig', array(
75 'token' => Session::getToken()
76 ));
77 if (isset($_GET['install'])) {
78 if (($_POST['password'] == $_POST['password_repeat'])
79 && $_POST['password'] != "" && $_POST['login'] != "") {
80 # let's rock, install poche baby !
81 $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
82 Session::logout();
83 Tools::logm('poche is now installed');
84 Tools::redirect();
85 }
86 else {
87 Tools::logm('error during installation');
88 Tools::redirect();
89 }
90 }
91 exit();
92 }
93
94 public function getDefaultConfig()
95 {
96 return array(
97 'pager' => PAGINATION,
98 'language' => LANG,
99 );
100 }
101
102 /**
103 * Call action (mark as fav, archive, delete, etc.)
104 */
105 public function action($action, Url $url, $id = 0, $import = FALSE)
106 {
107 switch ($action)
108 {
109 case 'add':
110 if($parametres_url = $url->fetchContent()) {
111 if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'], $this->user->getId())) {
112 Tools::logm('add link ' . $url->getUrl());
113 $sequence = '';
114 if (STORAGE == 'postgres') {
115 $sequence = 'entries_id_seq';
116 }
117 $last_id = $this->store->getLastId($sequence);
118 if (DOWNLOAD_PICTURES) {
119 $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id);
120 }
121 if (!$import) {
122 $this->messages->add('s', _('the link has been added successfully'));
123 }
124 }
125 else {
126 if (!$import) {
127 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
128 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
129 }
130 }
131 }
132 else {
133 if (!$import) {
134 $this->messages->add('e', _('error during fetching content : the link wasn\'t added'));
135 Tools::logm('error during content fetch ' . $url->getUrl());
136 }
137 }
138 if (!$import) {
139 Tools::redirect();
140 }
141 break;
142 case 'delete':
143 $msg = 'delete link #' . $id;
144 if ($this->store->deleteById($id, $this->user->getId())) {
145 if (DOWNLOAD_PICTURES) {
146 remove_directory(ABS_PATH . $id);
147 }
148 $this->messages->add('s', _('the link has been deleted successfully'));
149 }
150 else {
151 $this->messages->add('e', _('the link wasn\'t deleted'));
152 $msg = 'error : can\'t delete link #' . $id;
153 }
154 Tools::logm($msg);
155 Tools::redirect('?');
156 break;
157 case 'toggle_fav' :
158 $this->store->favoriteById($id, $this->user->getId());
159 Tools::logm('mark as favorite link #' . $id);
160 if (!$import) {
161 Tools::redirect();
162 }
163 break;
164 case 'toggle_archive' :
165 $this->store->archiveById($id, $this->user->getId());
166 Tools::logm('archive link #' . $id);
167 if (!$import) {
168 Tools::redirect();
169 }
170 break;
171 default:
172 break;
173 }
174 }
175
176 function displayView($view, $id = 0)
177 {
178 $tpl_vars = array();
179
180 switch ($view)
181 {
182 case 'config':
183 $dev = $this->getPocheVersion('dev');
184 $prod = $this->getPocheVersion('prod');
185 $compare_dev = version_compare(POCHE_VERSION, $dev);
186 $compare_prod = version_compare(POCHE_VERSION, $prod);
187 $tpl_vars = array(
188 'dev' => $dev,
189 'prod' => $prod,
190 'compare_dev' => $compare_dev,
191 'compare_prod' => $compare_prod,
192 );
193 Tools::logm('config view');
194 break;
195 case 'view':
196 $entry = $this->store->retrieveOneById($id, $this->user->getId());
197 if ($entry != NULL) {
198 Tools::logm('view link #' . $id);
199 $content = $entry['content'];
200 if (function_exists('tidy_parse_string')) {
201 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
202 $tidy->cleanRepair();
203 $content = $tidy->value;
204 }
205 $tpl_vars = array(
206 'entry' => $entry,
207 'content' => $content,
208 );
209 }
210 else {
211 Tools::logm('error in view call : entry is NULL');
212 }
213 break;
214 default: # home view
215 $entries = $this->store->getEntriesByView($view, $this->user->getId());
216 $this->pagination->set_total(count($entries));
217 $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&');
218 $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit());
219 $tpl_vars = array(
220 'entries' => $datas,
221 'page_links' => $page_links,
222 );
223 Tools::logm('display ' . $view . ' view');
224 break;
225 }
226
227 return $tpl_vars;
228 }
229
230 public function updatePassword()
231 {
232 if (MODE_DEMO) {
233 $this->messages->add('i', _('in demo mode, you can\'t update your password'));
234 Tools::logm('in demo mode, you can\'t do this');
235 Tools::redirect('?view=config');
236 }
237 else {
238 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
239 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
240 $this->messages->add('s', _('your password has been updated'));
241 $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
242 Session::logout();
243 Tools::logm('password updated');
244 Tools::redirect();
245 }
246 else {
247 $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
248 Tools::redirect('?view=config');
249 }
250 }
251 }
252 }
253
254 public function login($referer)
255 {
256 if (!empty($_POST['login']) && !empty($_POST['password'])) {
257 $user = $this->store->login($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']));
258 if ($user != array()) {
259 # Save login into Session
260 Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user)));
261
262 $this->messages->add('s', _('welcome to your poche'));
263 if (!empty($_POST['longlastingsession'])) {
264 $_SESSION['longlastingsession'] = 31536000;
265 $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession'];
266 session_set_cookie_params($_SESSION['longlastingsession']);
267 } else {
268 session_set_cookie_params(0);
269 }
270 session_regenerate_id(true);
271 Tools::logm('login successful');
272 Tools::redirect($referer);
273 }
274 $this->messages->add('e', _('login failed: bad login or password'));
275 Tools::logm('login failed');
276 Tools::redirect();
277 } else {
278 $this->messages->add('e', _('login failed: you have to fill all fields'));
279 Tools::logm('login failed');
280 Tools::redirect();
281 }
282 }
283
284 public function logout()
285 {
286 $this->user = array();
287 Session::logout();
288 $this->messages->add('s', _('see you soon!'));
289 Tools::logm('logout');
290 Tools::redirect();
291 }
292
293 private function importFromInstapaper()
294 {
295 # TODO gestion des articles favs
296 $html = new simple_html_dom();
297 $html->load_file('./instapaper-export.html');
298 Tools::logm('starting import from instapaper');
299
300 $read = 0;
301 $errors = array();
302 foreach($html->find('ol') as $ul)
303 {
304 foreach($ul->find('li') as $li)
305 {
306 $a = $li->find('a');
307 $url = new Url(base64_encode($a[0]->href));
308 $this->action('add', $url, 0, TRUE);
309 if ($read == '1') {
310 $sequence = '';
311 if (STORAGE == 'postgres') {
312 $sequence = 'entries_id_seq';
313 }
314 $last_id = $this->store->getLastId($sequence);
315 $this->action('toggle_archive', $url, $last_id, TRUE);
316 }
317 }
318
319 # the second <ol> is for read links
320 $read = 1;
321 }
322 $this->messages->add('s', _('import from instapaper completed'));
323 Tools::logm('import from instapaper completed');
324 Tools::redirect();
325 }
326
327 private function importFromPocket()
328 {
329 # TODO gestion des articles favs
330 $html = new simple_html_dom();
331 $html->load_file('./ril_export.html');
332 Tools::logm('starting import from pocket');
333
334 $read = 0;
335 $errors = array();
336 foreach($html->find('ul') as $ul)
337 {
338 foreach($ul->find('li') as $li)
339 {
340 $a = $li->find('a');
341 $url = new Url(base64_encode($a[0]->href));
342 $this->action('add', $url, 0, TRUE);
343 if ($read == '1') {
344 $sequence = '';
345 if (STORAGE == 'postgres') {
346 $sequence = 'entries_id_seq';
347 }
348 $last_id = $this->store->getLastId($sequence);
349 $this->action('toggle_archive', $url, $last_id, TRUE);
350 }
351 }
352
353 # the second <ul> is for read links
354 $read = 1;
355 }
356 $this->messages->add('s', _('import from pocket completed'));
357 Tools::logm('import from pocket completed');
358 Tools::redirect();
359 }
360
361 private function importFromReadability()
362 {
363 # TODO gestion des articles lus / favs
364 $str_data = file_get_contents("./readability");
365 $data = json_decode($str_data,true);
366 Tools::logm('starting import from Readability');
367
368 foreach ($data as $key => $value) {
369 $url = '';
370 foreach ($value as $attr => $attr_value) {
371 if ($attr == 'article__url') {
372 $url = new Url(base64_encode($attr_value));
373 }
374 $sequence = '';
375 if (STORAGE == 'postgres') {
376 $sequence = 'entries_id_seq';
377 }
378 // if ($attr_value == 'favorite' && $attr_value == 'true') {
379 // $last_id = $this->store->getLastId($sequence);
380 // $this->store->favoriteById($last_id);
381 // $this->action('toogle_fav', $url, $last_id, TRUE);
382 // }
383 if ($attr_value == 'archive' && $attr_value == 'true') {
384 $last_id = $this->store->getLastId($sequence);
385 $this->action('toggle_archive', $url, $last_id, TRUE);
386 }
387 }
388 if ($url->isCorrect())
389 $this->action('add', $url, 0, TRUE);
390 }
391 $this->messages->add('s', _('import from Readability completed'));
392 Tools::logm('import from Readability completed');
393 Tools::redirect();
394 }
395
396 public function import($from)
397 {
398 if ($from == 'pocket') {
399 $this->importFromPocket();
400 }
401 else if ($from == 'readability') {
402 $this->importFromReadability();
403 }
404 else if ($from == 'instapaper') {
405 $this->importFromInstapaper();
406 }
407 }
408
409 public function export()
410 {
411 $entries = $this->store->retrieveAll($this->user->getId());
412 echo $this->tpl->render('export.twig', array(
413 'export' => Tools::renderJson($entries),
414 ));
415 Tools::logm('export view');
416 }
417
418 private function getPocheVersion($which = 'prod')
419 {
420 $cache_file = CACHE . '/' . $which;
421 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
422 $version = file_get_contents($cache_file);
423 } else {
424 $version = file_get_contents('http://static.inthepoche.com/versions/' . $which);
425 file_put_contents($cache_file, $version, LOCK_EX);
426 }
427 return $version;
428 }
429} \ No newline at end of file
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php
new file mode 100644
index 00000000..d0e43166
--- /dev/null
+++ b/inc/poche/Tools.class.php
@@ -0,0 +1,226 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Tools
12{
13 public static function initPhp()
14 {
15 define('START_TIME', microtime(true));
16
17 if (phpversion() < 5) {
18 die(_('Oops, it seems you don\'t have PHP 5.'));
19 }
20
21 error_reporting(E_ALL);
22
23 function stripslashesDeep($value) {
24 return is_array($value)
25 ? array_map('stripslashesDeep', $value)
26 : stripslashes($value);
27 }
28
29 if (get_magic_quotes_gpc()) {
30 $_POST = array_map('stripslashesDeep', $_POST);
31 $_GET = array_map('stripslashesDeep', $_GET);
32 $_COOKIE = array_map('stripslashesDeep', $_COOKIE);
33 }
34
35 ob_start();
36 register_shutdown_function('ob_end_flush');
37 }
38
39 public static function getPocheUrl()
40 {
41 $https = (!empty($_SERVER['HTTPS'])
42 && (strtolower($_SERVER['HTTPS']) == 'on'))
43 || (isset($_SERVER["SERVER_PORT"])
44 && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
45 $serverport = (!isset($_SERVER["SERVER_PORT"])
46 || $_SERVER["SERVER_PORT"] == '80'
47 || ($https && $_SERVER["SERVER_PORT"] == '443')
48 ? '' : ':' . $_SERVER["SERVER_PORT"]);
49
50 $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
51
52 if (!isset($_SERVER["SERVER_NAME"])) {
53 return $scriptname;
54 }
55
56 return 'http' . ($https ? 's' : '') . '://'
57 . $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
58 }
59
60 public static function redirect($url = '')
61 {
62 if ($url === '') {
63 $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
64 if (isset($_POST['returnurl'])) {
65 $url = $_POST['returnurl'];
66 }
67 }
68
69 # prevent loop
70 if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
71 $url = Tools::getPocheUrl();
72 }
73
74 if (substr($url, 0, 1) !== '?') {
75 $ref = Tools::getPocheUrl();
76 if (substr($url, 0, strlen($ref)) !== $ref) {
77 $url = $ref;
78 }
79 }
80 self::logm('redirect to ' . $url);
81 header('Location: '.$url);
82 exit();
83 }
84
85 public static function getTplFile($view)
86 {
87 $tpl_file = 'home.twig';
88 switch ($view)
89 {
90 case 'install':
91 $tpl_file = 'install.twig';
92 break;
93 case 'import';
94 $tpl_file = 'import.twig';
95 break;
96 case 'export':
97 $tpl_file = 'export.twig';
98 break;
99 case 'config':
100 $tpl_file = 'config.twig';
101 break;
102 case 'view':
103 $tpl_file = 'view.twig';
104 break;
105 default:
106 break;
107 }
108 return $tpl_file;
109 }
110
111 public static function getFile($url)
112 {
113 $timeout = 15;
114 $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0";
115
116 if (in_array ('curl', get_loaded_extensions())) {
117 # Fetch feed from URL
118 $curl = curl_init();
119 curl_setopt($curl, CURLOPT_URL, $url);
120 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
121 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
122 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
123 curl_setopt($curl, CURLOPT_HEADER, false);
124
125 # for ssl, do not verified certificate
126 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
127 curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE );
128
129 # FeedBurner requires a proper USER-AGENT...
130 curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
131 curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate");
132 curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
133
134 $data = curl_exec($curl);
135 $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
136 $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
137 curl_close($curl);
138 } else {
139 # create http context and add timeout and user-agent
140 $context = stream_context_create(
141 array(
142 'http' => array(
143 'timeout' => $timeout,
144 'header' => "User-Agent: " . $useragent,
145 'follow_location' => true
146 ),
147 'ssl' => array(
148 'verify_peer' => false,
149 'allow_self_signed' => true
150 )
151 )
152 );
153
154 # only download page lesser than 4MB
155 $data = @file_get_contents($url, false, $context, -1, 4000000);
156
157 if (isset($http_response_header) and isset($http_response_header[0])) {
158 $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE));
159 }
160 }
161
162 # if response is not empty and response is OK
163 if (isset($data) and isset($httpcodeOK) and $httpcodeOK) {
164
165 # take charset of page and get it
166 preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
167
168 # if meta tag is found
169 if (!empty($meta[0])) {
170 preg_match('#charset="?(.*)"#si', $meta[0], $encoding);
171 # if charset is found set it otherwise, set it to utf-8
172 $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8';
173 } else {
174 $html_charset = 'utf-8';
175 $encoding[1] = '';
176 }
177
178 # replace charset of url to charset of page
179 $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data);
180
181 return $data;
182 }
183 else {
184 return FALSE;
185 }
186 }
187
188 public static function renderJson($data)
189 {
190 header('Cache-Control: no-cache, must-revalidate');
191 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
192 header('Content-type: application/json; charset=UTF-8');
193 echo json_encode($data);
194 exit();
195 }
196
197 public static function logm($message)
198 {
199 if (DEBUG_POCHE) {
200 $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n";
201 file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND);
202 error_log('DEBUG POCHE : ' . $message);
203 }
204 }
205
206 public static function encodeString($string)
207 {
208 return sha1($string . SALT);
209 }
210
211 public static function checkVar($var, $default = '')
212 {
213 return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default);
214 }
215
216 public static function getDomain($url)
217 {
218 $pieces = parse_url($url);
219 $domain = isset($pieces['host']) ? $pieces['host'] : '';
220 if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
221 return $regs['domain'];
222 }
223
224 return FALSE;
225 }
226} \ No newline at end of file
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php
new file mode 100644
index 00000000..f4a8f99e
--- /dev/null
+++ b/inc/poche/Url.class.php
@@ -0,0 +1,94 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Url
12{
13 public $url;
14
15 function __construct($url)
16 {
17 $this->url = base64_decode($url);
18 }
19
20 public function getUrl() {
21 return $this->url;
22 }
23
24 public function setUrl($url) {
25 $this->url = $url;
26 }
27
28 public function isCorrect()
29 {
30 $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
31
32 return preg_match($pattern, $this->url);
33 }
34
35 public function clean()
36 {
37 $url = html_entity_decode(trim($this->url));
38
39 $stuff = strpos($url,'&utm_source=');
40 if ($stuff !== FALSE)
41 $url = substr($url, 0, $stuff);
42 $stuff = strpos($url,'?utm_source=');
43 if ($stuff !== FALSE)
44 $url = substr($url, 0, $stuff);
45 $stuff = strpos($url,'#xtor=RSS-');
46 if ($stuff !== FALSE)
47 $url = substr($url, 0, $stuff);
48
49 $this->url = $url;
50 }
51
52 public function fetchContent()
53 {
54 if ($this->isCorrect()) {
55 $this->clean();
56 $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
57
58 # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol
59 if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) {
60 $this->setUrl('http://' . $this->getUrl());
61 $html = Encoding::toUTF8(Tools::getFile($this->getUrl()));
62 }
63
64 if (function_exists('tidy_parse_string')) {
65 $tidy = tidy_parse_string($html, array(), 'UTF8');
66 $tidy->cleanRepair();
67 $html = $tidy->value;
68 }
69
70 $parameters = array();
71 if (isset($html) and strlen($html) > 0)
72 {
73 $readability = new Readability($html, $this->getUrl());
74 $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES;
75 $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS;
76
77 if($readability->init())
78 {
79 $content = $readability->articleContent->innerHTML;
80 $parameters['title'] = $readability->articleTitle->innerHTML;
81 $parameters['content'] = $content;
82
83 return $parameters;
84 }
85 }
86 }
87 else {
88 #$msg->add('e', _('error during url preparation : the link is not valid'));
89 Tools::logm($this->getUrl() . ' is not a valid url');
90 }
91
92 return FALSE;
93 }
94} \ No newline at end of file
diff --git a/inc/poche/User.class.php b/inc/poche/User.class.php
new file mode 100644
index 00000000..6dac7839
--- /dev/null
+++ b/inc/poche/User.class.php
@@ -0,0 +1,50 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class User
12{
13 public $id;
14 public $username;
15 public $name;
16 public $password;
17 public $email;
18 public $config;
19
20 function __construct($user = array())
21 {
22 if ($user != array()) {
23 $this->id = $user['id'];
24 $this->username = $user['username'];
25 $this->name = $user['name'];
26 $this->password = $user['password'];
27 $this->email = $user['email'];
28 $this->config = $user['config'];
29 }
30 }
31
32 public function getId()
33 {
34 return $this->id;
35 }
36
37 public function getUsername()
38 {
39 return $this->username;
40 }
41
42 public function setConfig($config)
43 {
44 $this->config = $config;
45 }
46
47 public function getConfigValue($name) {
48 return (isset($this->config[$name])) ? $this->config[$name] : FALSE;
49 }
50} \ No newline at end of file
diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php
new file mode 100644
index 00000000..930b31e5
--- /dev/null
+++ b/inc/poche/config.inc.php
@@ -0,0 +1,61 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <nicolas@loeuillet.org>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11# storage
12define ('STORAGE','sqlite'); # postgres, mysql, sqlite
13define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite
14define ('STORAGE_DB', 'poche'); # only for postgres & mysql
15define ('STORAGE_SQLITE', './db/poche.sqlite');
16define ('STORAGE_USER', 'postgres'); # leave blank for sqlite
17define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite
18
19define ('POCHE_VERSION', '1.0-beta');
20define ('MODE_DEMO', FALSE);
21define ('DEBUG_POCHE', FALSE);
22define ('CONVERT_LINKS_FOOTNOTES', FALSE);
23define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE);
24define ('DOWNLOAD_PICTURES', FALSE);
25define ('SHARE_TWITTER', TRUE);
26define ('SHARE_MAIL', TRUE);
27define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX');
28define ('ABS_PATH', 'assets/');
29define ('TPL', './tpl');
30define ('LOCALE', './locale');
31define ('CACHE', './cache');
32define ('LANG', 'en_EN.UTF8');
33define ('PAGINATION', '10');
34define ('THEME', 'light');
35
36# /!\ Be careful if you change the lines below /!\
37require_once './inc/poche/User.class.php';
38require_once './inc/poche/Tools.class.php';
39require_once './inc/poche/Url.class.php';
40require_once './inc/3rdparty/class.messages.php';
41require_once './inc/poche/Poche.class.php';
42require_once './inc/3rdparty/Readability.php';
43require_once './inc/3rdparty/Encoding.php';
44require_once './inc/poche/Database.class.php';
45require_once './vendor/autoload.php';
46require_once './inc/3rdparty/simple_html_dom.php';
47require_once './inc/3rdparty/paginator.php';
48require_once './inc/3rdparty/Session.class.php';
49
50if (DOWNLOAD_PICTURES) {
51 require_once './inc/poche/pochePictures.php';
52}
53
54$poche = new Poche();
55#XSRF protection with token
56// if (!empty($_POST)) {
57// if (!Session::isToken($_POST['token'])) {
58// die(_('Wrong token'));
59// }
60// unset($_SESSION['tokens']);
61// } \ No newline at end of file
diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php
new file mode 100644
index 00000000..4e4a0b08
--- /dev/null
+++ b/inc/poche/pochePictures.php
@@ -0,0 +1,110 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11/**
12 * On modifie les URLS des images dans le corps de l'article
13 */
14function filtre_picture($content, $url, $id)
15{
16 $matches = array();
17 preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
18 foreach($matches as $i => $link) {
19 $link[1] = trim($link[1]);
20 if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) {
21 $absolute_path = get_absolute_link($link[2],$url);
22 $filename = basename(parse_url($absolute_path, PHP_URL_PATH));
23 $directory = create_assets_directory($id);
24 $fullpath = $directory . '/' . $filename;
25 download_pictures($absolute_path, $fullpath);
26 $content = str_replace($matches[$i][2], $fullpath, $content);
27 }
28
29 }
30
31 return $content;
32}
33
34/**
35 * Retourne le lien absolu
36 */
37function get_absolute_link($relative_link, $url) {
38 /* return if already absolute URL */
39 if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
40
41 /* queries and anchors */
42 if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link;
43
44 /* parse base URL and convert to local variables:
45 $scheme, $host, $path */
46 extract(parse_url($url));
47
48 /* remove non-directory element from path */
49 $path = preg_replace('#/[^/]*$#', '', $path);
50
51 /* destroy path if relative url points to root */
52 if ($relative_link[0] == '/') $path = '';
53
54 /* dirty absolute URL */
55 $abs = $host . $path . '/' . $relative_link;
56
57 /* replace '//' or '/./' or '/foo/../' with '/' */
58 $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
59 for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
60
61 /* absolute URL is ready! */
62 return $scheme.'://'.$abs;
63}
64
65/**
66 * Téléchargement des images
67 */
68function download_pictures($absolute_path, $fullpath)
69{
70 $rawdata = Tools::getFile($absolute_path);
71
72 if(file_exists($fullpath)) {
73 unlink($fullpath);
74 }
75 $fp = fopen($fullpath, 'x');
76 fwrite($fp, $rawdata);
77 fclose($fp);
78}
79
80/**
81 * Crée un répertoire de médias pour l'article
82 */
83function create_assets_directory($id)
84{
85 $assets_path = ABS_PATH;
86 if(!is_dir($assets_path)) {
87 mkdir($assets_path, 0705);
88 }
89
90 $article_directory = $assets_path . $id;
91 if(!is_dir($article_directory)) {
92 mkdir($article_directory, 0705);
93 }
94
95 return $article_directory;
96}
97
98/**
99 * Suppression du répertoire d'images
100 */
101function remove_directory($directory)
102{
103 if(is_dir($directory)) {
104 $files = array_diff(scandir($directory), array('.','..'));
105 foreach ($files as $file) {
106 (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
107 }
108 return rmdir($directory);
109 }
110} \ No newline at end of file
diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php
deleted file mode 100644
index 6522c798..00000000
--- a/inc/rain.tpl.class.php
+++ /dev/null
@@ -1,1043 +0,0 @@
1<?php
2
3/**
4 * RainTPL
5 * -------
6 * Realized by Federico Ulfo & maintained by the Rain Team
7 * Distributed under GNU/LGPL 3 License
8 *
9 * @version 2.7.2
10 */
11
12
13class RainTPL{
14
15 // -------------------------
16 // CONFIGURATION
17 // -------------------------
18
19 /**
20 * Template directory
21 *
22 * @var string
23 */
24 static $tpl_dir = "tpl/";
25
26
27 /**
28 * Cache directory. Is the directory where RainTPL will compile the template and save the cache
29 *
30 * @var string
31 */
32 static $cache_dir = "tmp/";
33
34
35 /**
36 * Template base URL. RainTPL will add this URL to the relative paths of element selected in $path_replace_list.
37 *
38 * @var string
39 */
40 static $base_url = null;
41
42
43 /**
44 * Template extension.
45 *
46 * @var string
47 */
48 static $tpl_ext = "html";
49
50
51 /**
52 * Path replace is a cool features that replace all relative paths of images (<img src="...">), stylesheet (<link href="...">), script (<script src="...">) and link (<a href="...">)
53 * Set true to enable the path replace.
54 *
55 * @var unknown_type
56 */
57 static $path_replace = true;
58
59
60 /**
61 * You can set what the path_replace method will replace.
62 * Avaible options: a, img, link, script, input
63 *
64 * @var array
65 */
66 static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' );
67
68
69 /**
70 * You can define in the black list what string are disabled into the template tags
71 *
72 * @var unknown_type
73 */
74 static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV', 'eval', 'exec', 'unlink', 'rmdir' );
75
76
77 /**
78 * Check template.
79 * true: checks template update time, if changed it compile them
80 * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
81 *
82 */
83 static $check_template_update = true;
84
85
86 /**
87 * PHP tags <? ?>
88 * True: php tags are enabled into the template
89 * False: php tags are disabled into the template and rendered as html
90 *
91 * @var bool
92 */
93 static $php_enabled = true;
94
95
96 /**
97 * Debug mode flag.
98 * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated.
99 * False: exception is thrown on found error.
100 *
101 * @var bool
102 */
103 static $debug = false;
104
105 // -------------------------
106
107
108 // -------------------------
109 // RAINTPL VARIABLES
110 // -------------------------
111
112 /**
113 * Is the array where RainTPL keep the variables assigned
114 *
115 * @var array
116 */
117 public $var = array();
118
119 protected $tpl = array(), // variables to keep the template directories and info
120 $cache = false, // static cache enabled / disabled
121 $cache_id = null; // identify only one cache
122
123 protected static $config_name_sum = array(); // takes all the config to create the md5 of the file
124
125 // -------------------------
126
127
128
129 const CACHE_EXPIRE_TIME = 3600; // default cache expire time = hour
130
131
132
133 /**
134 * Assign variable
135 * eg. $t->assign('name','mickey');
136 *
137 * @param mixed $variable_name Name of template variable or associative array name/value
138 * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array
139 */
140
141 function assign( $variable, $value = null ){
142 if( is_array( $variable ) )
143 $this->var += $variable;
144 else
145 $this->var[ $variable ] = $value;
146 }
147
148
149
150 /**
151 * Draw the template
152 * eg. $html = $tpl->draw( 'demo', TRUE ); // return template in string
153 * or $tpl->draw( $tpl_name ); // echo the template
154 *
155 * @param string $tpl_name template to load
156 * @param boolean $return_string true=return a string, false=echo the template
157 * @return string
158 */
159
160 function draw( $tpl_name, $return_string = false ){
161
162 try {
163 // compile the template if necessary and set the template filepath
164 $this->check_template( $tpl_name );
165 } catch (RainTpl_Exception $e) {
166 $output = $this->printDebug($e);
167 die($output);
168 }
169
170 // Cache is off and, return_string is false
171 // Rain just echo the template
172
173 if( !$this->cache && !$return_string ){
174 extract( $this->var );
175 include $this->tpl['compiled_filename'];
176 unset( $this->tpl );
177 }
178
179
180 // cache or return_string are enabled
181 // rain get the output buffer to save the output in the cache or to return it as string
182
183 else{
184
185 //----------------------
186 // get the output buffer
187 //----------------------
188 ob_start();
189 extract( $this->var );
190 include $this->tpl['compiled_filename'];
191 $raintpl_contents = ob_get_clean();
192 //----------------------
193
194
195 // save the output in the cache
196 if( $this->cache )
197 file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
198
199 // free memory
200 unset( $this->tpl );
201
202 // return or print the template
203 if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
204
205 }
206
207 }
208
209
210
211 /**
212 * If exists a valid cache for this template it returns the cache
213 *
214 * @param string $tpl_name Name of template (set the same of draw)
215 * @param int $expiration_time Set after how many seconds the cache expire and must be regenerated
216 * @return string it return the HTML or null if the cache must be recreated
217 */
218
219 function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = null ){
220
221 // set the cache_id
222 $this->cache_id = $cache_id;
223
224 if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
225 return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
226 else{
227 //delete the cache of the selected template
228 if (file_exists($this->tpl['cache_filename']))
229 unlink($this->tpl['cache_filename'] );
230 $this->cache = true;
231 }
232 }
233
234
235
236 /**
237 * Configure the settings of RainTPL
238 *
239 */
240 static function configure( $setting, $value = null ){
241 if( is_array( $setting ) )
242 foreach( $setting as $key => $value )
243 self::configure( $key, $value );
244 else if( property_exists( __CLASS__, $setting ) ){
245 self::$$setting = $value;
246 self::$config_name_sum[ $setting ] = $value; // take trace of all config
247 }
248 }
249
250
251
252 // check if has to compile the template
253 // return true if the template has changed
254 protected function check_template( $tpl_name ){
255
256 if( !isset($this->tpl['checked']) ){
257
258 $tpl_basename = basename( $tpl_name ); // template basename
259 $tpl_basedir = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null; // template basedirectory
260 $tpl_dir = self::$tpl_dir . $tpl_basedir; // template directory
261 $this->tpl['tpl_filename'] = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext; // template filename
262 $temp_compiled_filename = self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
263 $this->tpl['compiled_filename'] = $temp_compiled_filename . '.rtpl.php'; // cache filename
264 $this->tpl['cache_filename'] = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php'; // static cache filename
265
266 // if the template doesn't exsist throw an error
267 if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
268 $e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' );
269 throw $e->setTemplateFile($this->tpl['tpl_filename']);
270 }
271
272 // file doesn't exsist, or the template was updated, Rain will compile the template
273 if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){
274 $this->compileFile( $tpl_basename, $tpl_basedir, $this->tpl['tpl_filename'], self::$cache_dir, $this->tpl['compiled_filename'] );
275 return true;
276 }
277 $this->tpl['checked'] = true;
278 }
279 }
280
281
282 /**
283 * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
284 * @access protected
285 */
286 protected function xml_reSubstitution($capture) {
287 return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
288 }
289
290 /**
291 * Compile and write the compiled template file
292 * @access protected
293 */
294 protected function compileFile( $tpl_basename, $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){
295
296 //read template file
297 $this->tpl['source'] = $template_code = file_get_contents( $tpl_filename );
298
299 //xml substitution
300 $template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code );
301
302 //disable php tag
303 if( !self::$php_enabled )
304 $template_code = str_replace( array("<?","?>"), array("&lt;?","?&gt;"), $template_code );
305
306 //xml re-substitution
307 $template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code );
308
309 //compile template
310 $template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir );
311
312
313 // fix the php-eating-newline-after-closing-tag-problem
314 $template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled );
315
316 // create directories
317 if( !is_dir( $cache_dir ) )
318 mkdir( $cache_dir, 0755, true );
319
320 if( !is_writable( $cache_dir ) )
321 throw new RainTpl_Exception ('Cache directory ' . $cache_dir . 'doesn\'t have write permission. Set write permission or set RAINTPL_CHECK_TEMPLATE_UPDATE to false. More details on http://www.raintpl.com/Documentation/Documentation-for-PHP-developers/Configuration/');
322
323 //write compiled file
324 file_put_contents( $compiled_filename, $template_compiled );
325 }
326
327
328
329 /**
330 * Compile template
331 * @access protected
332 */
333 protected function compileTemplate( $template_code, $tpl_basedir ){
334
335 //tag list
336 $tag_regexp = array( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
337 'loop_close' => '(\{\/loop\})',
338 'if' => '(\{if(?: condition){0,1}="[^"]*"\})',
339 'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})',
340 'else' => '(\{else\})',
341 'if_close' => '(\{\/if\})',
342 'function' => '(\{function="[^"]*"\})',
343 'noparse' => '(\{noparse\})',
344 'noparse_close'=> '(\{\/noparse\})',
345 'ignore' => '(\{ignore\}|\{\*)',
346 'ignore_close' => '(\{\/ignore\}|\*\})',
347 'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
348 'template_info'=> '(\{\$template_info\})',
349 'function' => '(\{function="(\w*?)(?:.*?)"\})'
350 );
351
352 $tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
353
354 //split the code with the tags regexp
355 $template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
356
357 //path replace (src of img, background and href of link)
358 $template_code = $this->path_replace( $template_code, $tpl_basedir );
359
360 //compile the code
361 $compiled_code = $this->compileCode( $template_code );
362
363 //return the compiled code
364 return $compiled_code;
365
366 }
367
368
369
370 /**
371 * Compile the code
372 * @access protected
373 */
374 protected function compileCode( $parsed_code ){
375
376 //variables initialization
377 $compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
378 $loop_level = 0;
379
380 //read all parsed code
381 while( $html = array_shift( $parsed_code ) ){
382
383 //close ignore tag
384 if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
385 $ignore_is_open = false;
386
387 //code between tag ignore id deleted
388 elseif( $ignore_is_open ){
389 //ignore the code
390 }
391
392 //close no parse tag
393 elseif( strpos( $html, '{/noparse}' ) !== FALSE )
394 $comment_is_open = false;
395
396 //code between tag noparse is not compiled
397 elseif( $comment_is_open )
398 $compiled_code .= $html;
399
400 //ignore
401 elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
402 $ignore_is_open = true;
403
404 //noparse
405 elseif( strpos( $html, '{noparse}' ) !== FALSE )
406 $comment_is_open = true;
407
408 //include tag
409 elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
410
411 //variables substitution
412 $include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level );
413
414 // if the cache is active
415 if( isset($code[ 2 ]) ){
416
417 //dynamic include
418 $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
419 'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' .
420 ' echo $cache;' .
421 'else{' .
422 ' $tpl_dir_temp = self::$tpl_dir;' .
423 ' $tpl->assign( $this->var );' .
424 ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
425 ' $tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
426 '} ?>';
427 }
428 else{
429
430 //dynamic include
431 $compiled_code .= '<?php $tpl = new '.get_class($this).';' .
432 '$tpl_dir_temp = self::$tpl_dir;' .
433 '$tpl->assign( $this->var );' .
434 ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
435 '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
436 '?>';
437
438
439 }
440
441 }
442
443 //loop
444 elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
445
446 //increase the loop counter
447 $loop_level++;
448
449 //replace the variable in the loop
450 $var = $this->var_replace( '$' . $code[ 1 ], $tag_left_delimiter=null, $tag_right_delimiter=null, $php_left_delimiter=null, $php_right_delimiter=null, $loop_level-1 );
451
452 //loop variables
453 $counter = "\$counter$loop_level"; // count iteration
454 $key = "\$key$loop_level"; // key
455 $value = "\$value$loop_level"; // value
456
457 //loop code
458 $compiled_code .= "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
459
460 }
461
462 //close loop tag
463 elseif( strpos( $html, '{/loop}' ) !== FALSE ) {
464
465 //iterator
466 $counter = "\$counter$loop_level";
467
468 //decrease the loop counter
469 $loop_level--;
470
471 //close loop code
472 $compiled_code .= "<?php } ?>";
473
474 }
475
476 //if
477 elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
478
479 //increase open if counter (for intendation)
480 $open_if++;
481
482 //tag
483 $tag = $code[ 0 ];
484
485 //condition attribute
486 $condition = $code[ 1 ];
487
488 // check if there's any function disabled by black_list
489 $this->function_check( $tag );
490
491 //variable substitution into condition (no delimiter into the condition)
492 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
493
494 //if code
495 $compiled_code .= "<?php if( $parsed_condition ){ ?>";
496
497 }
498
499 //elseif
500 elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
501
502 //tag
503 $tag = $code[ 0 ];
504
505 //condition attribute
506 $condition = $code[ 1 ];
507
508 //variable substitution into condition (no delimiter into the condition)
509 $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
510
511 //elseif code
512 $compiled_code .= "<?php }elseif( $parsed_condition ){ ?>";
513 }
514
515 //else
516 elseif( strpos( $html, '{else}' ) !== FALSE ) {
517
518 //else code
519 $compiled_code .= '<?php }else{ ?>';
520
521 }
522
523 //close if tag
524 elseif( strpos( $html, '{/if}' ) !== FALSE ) {
525
526 //decrease if counter
527 $open_if--;
528
529 // close if code
530 $compiled_code .= '<?php } ?>';
531
532 }
533
534 //function
535 elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){
536
537 //tag
538 $tag = $code[ 0 ];
539
540 //function
541 $function = $code[ 1 ];
542
543 // check if there's any function disabled by black_list
544 $this->function_check( $tag );
545
546 if( empty( $code[ 2 ] ) )
547 $parsed_function = $function . "()";
548 else
549 // parse the function
550 $parsed_function = $function . $this->var_replace( $code[ 2 ], $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
551
552 //if code
553 $compiled_code .= "<?php echo $parsed_function; ?>";
554 }
555
556 // show all vars
557 elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) {
558
559 //tag
560 $tag = '{$template_info}';
561
562 //if code
563 $compiled_code .= '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>';
564 }
565
566
567 //all html code
568 else{
569
570 //variables substitution (es. {$title})
571 $html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
572 //const substitution (es. {#CONST#})
573 $html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
574 //functions substitution (es. {"string"|functions})
575 $compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
576 }
577 }
578
579 if( $open_if > 0 ) {
580 $e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template');
581 throw $e->setTemplateFile($this->tpl['tpl_filename']);
582 }
583 return $compiled_code;
584 }
585
586
587 /**
588 * Reduce a path, eg. www/library/../filepath//file => www/filepath/file
589 * @param type $path
590 * @return type
591 */
592 protected function reduce_path( $path ){
593 $path = str_replace( "://", "@not_replace@", $path );
594 $path = str_replace( "//", "/", $path );
595 $path = str_replace( "@not_replace@", "://", $path );
596 return preg_replace('/\w+\/\.\.\//', '', $path );
597 }
598
599
600
601 /**
602 * replace the path of image src, link href and a href.
603 * url => template_dir/url
604 * url# => url
605 * http://url => http://url
606 *
607 * @param string $html
608 * @return string html sostituito
609 */
610 protected function path_replace( $html, $tpl_basedir ){
611
612 if( self::$path_replace ){
613
614 $tpl_dir = self::$base_url . self::$tpl_dir . $tpl_basedir;
615
616 // reduce the path
617 $path = $this->reduce_path($tpl_dir);
618
619 $exp = $sub = array();
620
621 if( in_array( "img", self::$path_replace_list ) ){
622 $exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
623 $sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' );
624 }
625
626 if( in_array( "script", self::$path_replace_list ) ){
627 $exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
628 $sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) );
629 }
630
631 if( in_array( "link", self::$path_replace_list ) ){
632 $exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
633 $sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) );
634 }
635
636 if( in_array( "a", self::$path_replace_list ) ){
637 $exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
638 $sub = array_merge( $sub , array( '<a$1href=@$2$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) );
639 }
640
641 if( in_array( "input", self::$path_replace_list ) ){
642 $exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
643 $sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) );
644 }
645
646 return preg_replace( $exp, $sub, $html );
647
648 }
649 else
650 return $html;
651
652 }
653
654
655
656
657
658 // replace const
659 function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
660 // const
661 return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html );
662 }
663
664
665
666 // replace functions/modifiers on constants and strings
667 function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
668
669 preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches );
670
671 for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){
672
673 //complete tag ex: {$news.title|substr:0,100}
674 $tag = $matches[ 0 ][ $i ];
675
676 //variable name ex: news.title
677 $var = $matches[ 1 ][ $i ];
678
679 //function and parameters associate to the variable ex: substr:0,100
680 $extra_var = $matches[ 2 ][ $i ];
681
682 // check if there's any function disabled by black_list
683 $this->function_check( $tag );
684
685 $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
686
687
688 // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
689 $is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var );
690
691 //function associate to variable
692 $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
693
694 //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
695 $temp = preg_split( "/\.|\[|\-\>/", $var );
696
697 //variable name
698 $var_name = $temp[ 0 ];
699
700 //variable path
701 $variable_path = substr( $var, strlen( $var_name ) );
702
703 //parentesis transform [ e ] in [" e in "]
704 $variable_path = str_replace( '[', '["', $variable_path );
705 $variable_path = str_replace( ']', '"]', $variable_path );
706
707 //transform .$variable in ["$variable"]
708 $variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
709
710 //transform [variable] in ["variable"]
711 $variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
712
713 //if there's a function
714 if( $function_var ){
715
716 // check if there's a function or a static method and separate, function by parameters
717 $function_var = str_replace("::", "@double_dot@", $function_var );
718
719 // get the position of the first :
720 if( $dot_position = strpos( $function_var, ":" ) ){
721
722 // get the function and the parameters
723 $function = substr( $function_var, 0, $dot_position );
724 $params = substr( $function_var, $dot_position+1 );
725
726 }
727 else{
728
729 //get the function
730 $function = str_replace( "@double_dot@", "::", $function_var );
731 $params = null;
732
733 }
734
735 // replace back the @double_dot@ with ::
736 $function = str_replace( "@double_dot@", "::", $function );
737 $params = str_replace( "@double_dot@", "::", $params );
738
739
740 }
741 else
742 $function = $params = null;
743
744 $php_var = $var_name . $variable_path;
745
746 // compile the variable for php
747 if( isset( $function ) ){
748 if( $php_var )
749 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
750 else
751 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter;
752 }
753 else
754 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
755
756 $html = str_replace( $tag, $php_var, $html );
757
758 }
759
760 return $html;
761
762 }
763
764
765
766 function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
767
768 //all variables
769 if( preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/', $html, $matches ) ){
770
771 for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
772 $parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
773
774 foreach( $parsed as $tag => $array ){
775
776 //variable name ex: news.title
777 $var = $array['var'];
778
779 //function and parameters associate to the variable ex: substr:0,100
780 $extra_var = $array['extra_var'];
781
782 // check if there's any function disabled by black_list
783 $this->function_check( $tag );
784
785 $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
786
787 // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
788 $is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
789
790 //function associate to variable
791 $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
792
793 //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
794 $temp = preg_split( "/\.|\[|\-\>/", $var );
795
796 //variable name
797 $var_name = $temp[ 0 ];
798
799 //variable path
800 $variable_path = substr( $var, strlen( $var_name ) );
801
802 //parentesis transform [ e ] in [" e in "]
803 $variable_path = str_replace( '[', '["', $variable_path );
804 $variable_path = str_replace( ']', '"]', $variable_path );
805
806 //transform .$variable in ["$variable"] and .variable in ["variable"]
807 $variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
808
809 // if is an assignment also assign the variable to $this->var['value']
810 if( $is_init_variable )
811 $extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
812
813
814
815 //if there's a function
816 if( $function_var ){
817
818 // check if there's a function or a static method and separate, function by parameters
819 $function_var = str_replace("::", "@double_dot@", $function_var );
820
821
822 // get the position of the first :
823 if( $dot_position = strpos( $function_var, ":" ) ){
824
825 // get the function and the parameters
826 $function = substr( $function_var, 0, $dot_position );
827 $params = substr( $function_var, $dot_position+1 );
828
829 }
830 else{
831
832 //get the function
833 $function = str_replace( "@double_dot@", "::", $function_var );
834 $params = null;
835
836 }
837
838 // replace back the @double_dot@ with ::
839 $function = str_replace( "@double_dot@", "::", $function );
840 $params = str_replace( "@double_dot@", "::", $params );
841 }
842 else
843 $function = $params = null;
844
845 //if it is inside a loop
846 if( $loop_level ){
847 //verify the variable name
848 if( $var_name == 'key' )
849 $php_var = '$key' . $loop_level;
850 elseif( $var_name == 'value' )
851 $php_var = '$value' . $loop_level . $variable_path;
852 elseif( $var_name == 'counter' )
853 $php_var = '$counter' . $loop_level;
854 else
855 $php_var = '$' . $var_name . $variable_path;
856 }else
857 $php_var = '$' . $var_name . $variable_path;
858
859 // compile the variable for php
860 if( isset( $function ) )
861 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
862 else
863 $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
864
865 $html = str_replace( $tag, $php_var, $html );
866
867
868 }
869 }
870
871 return $html;
872 }
873
874
875
876 /**
877 * Check if function is in black list (sandbox)
878 *
879 * @param string $code
880 * @param string $tag
881 */
882 protected function function_check( $code ){
883
884 $preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#';
885
886 // check if the function is in the black list (or not in white list)
887 if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){
888
889 // find the line of the error
890 $line = 0;
891 $rows=explode("\n",$this->tpl['source']);
892 while( !strpos($rows[$line],$code) )
893 $line++;
894
895 // stop the execution of the script
896 $e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template');
897 throw $e->setTemplateFile($this->tpl['tpl_filename'])
898 ->setTag($code)
899 ->setTemplateLine($line);
900 }
901
902 }
903
904 /**
905 * Prints debug info about exception or passes it further if debug is disabled.
906 *
907 * @param RainTpl_Exception $e
908 * @return string
909 */
910 protected function printDebug(RainTpl_Exception $e){
911 if (!self::$debug) {
912 throw $e;
913 }
914 $output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>',
915 get_class($e),
916 $e->getMessage(),
917 $e->getTemplateFile()
918 );
919 if ($e instanceof RainTpl_SyntaxException) {
920 if (null != $e->getTemplateLine()) {
921 $output .= '<p>line: ' . $e->getTemplateLine() . '</p>';
922 }
923 if (null != $e->getTag()) {
924 $output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>';
925 }
926 if (null != $e->getTemplateLine() && null != $e->getTag()) {
927 $rows=explode("\n", htmlspecialchars($this->tpl['source']));
928 $rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>';
929 $output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>';
930 }
931 }
932 $output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>',
933 $e->getFile(), $e->getLine(),
934 nl2br(htmlspecialchars($e->getTraceAsString()))
935 );
936 return $output;
937 }
938}
939
940
941/**
942 * Basic Rain tpl exception.
943 */
944class RainTpl_Exception extends Exception{
945 /**
946 * Path of template file with error.
947 */
948 protected $templateFile = '';
949
950 /**
951 * Returns path of template file with error.
952 *
953 * @return string
954 */
955 public function getTemplateFile()
956 {
957 return $this->templateFile;
958 }
959
960 /**
961 * Sets path of template file with error.
962 *
963 * @param string $templateFile
964 * @return RainTpl_Exception
965 */
966 public function setTemplateFile($templateFile)
967 {
968 $this->templateFile = (string) $templateFile;
969 return $this;
970 }
971}
972
973/**
974 * Exception thrown when template file does not exists.
975 */
976class RainTpl_NotFoundException extends RainTpl_Exception{
977}
978
979/**
980 * Exception thrown when syntax error occurs.
981 */
982class RainTpl_SyntaxException extends RainTpl_Exception{
983 /**
984 * Line in template file where error has occured.
985 *
986 * @var int | null
987 */
988 protected $templateLine = null;
989
990 /**
991 * Tag which caused an error.
992 *
993 * @var string | null
994 */
995 protected $tag = null;
996
997 /**
998 * Returns line in template file where error has occured
999 * or null if line is not defined.
1000 *
1001 * @return int | null
1002 */
1003 public function getTemplateLine()
1004 {
1005 return $this->templateLine;
1006 }
1007
1008 /**
1009 * Sets line in template file where error has occured.
1010 *
1011 * @param int $templateLine
1012 * @return RainTpl_SyntaxException
1013 */
1014 public function setTemplateLine($templateLine)
1015 {
1016 $this->templateLine = (int) $templateLine;
1017 return $this;
1018 }
1019
1020 /**
1021 * Returns tag which caused an error.
1022 *
1023 * @return string
1024 */
1025 public function getTag()
1026 {
1027 return $this->tag;
1028 }
1029
1030 /**
1031 * Sets tag which caused an error.
1032 *
1033 * @param string $tag
1034 * @return RainTpl_SyntaxException
1035 */
1036 public function setTag($tag)
1037 {
1038 $this->tag = (string) $tag;
1039 return $this;
1040 }
1041}
1042
1043// -- end
diff --git a/inc/store/file.class.php b/inc/store/file.class.php
deleted file mode 100644
index ad20937d..00000000
--- a/inc/store/file.class.php
+++ /dev/null
@@ -1,51 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class File extends Store {
12 function __construct() {
13
14 }
15
16 public function add() {
17
18 }
19
20 public function retrieveOneById($id) {
21
22 }
23
24 public function retrieveOneByURL($url) {
25
26 }
27
28 public function deleteById($id) {
29
30 }
31
32 public function favoriteById($id) {
33
34 }
35
36 public function archiveById($id) {
37
38 }
39
40 public function getEntriesByView($view) {
41
42 }
43
44 public function getLastId() {
45
46 }
47
48 public function updateContentById($id) {
49
50 }
51}
diff --git a/inc/store/sqlite.class.php b/inc/store/sqlite.class.php
deleted file mode 100644
index 21081608..00000000
--- a/inc/store/sqlite.class.php
+++ /dev/null
@@ -1,202 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Sqlite extends Store {
12
13 public static $db_path = 'sqlite:./db/poche.sqlite';
14 var $handle;
15
16 function __construct() {
17 parent::__construct();
18
19 $this->handle = new PDO(self::$db_path);
20 $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
21 }
22
23 private function getHandle() {
24 return $this->handle;
25 }
26
27 public function isInstalled() {
28 $sql = "SELECT name FROM sqlite_sequence WHERE name=?";
29 $query = $this->executeQuery($sql, array('config'));
30 $hasConfig = $query->fetchAll();
31
32 if (count($hasConfig) == 0)
33 return FALSE;
34
35 if (!$this->getLogin() || !$this->getPassword())
36 return FALSE;
37
38 return TRUE;
39 }
40
41 public function install($login, $password) {
42 $this->getHandle()->exec('CREATE TABLE IF NOT EXISTS "config" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "name" VARCHAR UNIQUE, "value" BLOB)');
43
44 $this->handle->exec('CREATE TABLE IF NOT EXISTS "entries" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "title" VARCHAR, "url" VARCHAR UNIQUE , "is_read" INTEGER DEFAULT 0, "is_fav" INTEGER DEFAULT 0, "content" BLOB)');
45
46 if (!$this->getLogin()) {
47 $sql_login = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
48 $params_login = array('login', $login);
49 $query = $this->executeQuery($sql_login, $params_login);
50 }
51
52 if (!$this->getPassword()) {
53 $sql_pass = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
54 $params_pass = array('password', $password);
55 $query = $this->executeQuery($sql_pass, $params_pass);
56 }
57
58 return TRUE;
59 }
60
61 public function getLogin() {
62 $sql = "SELECT value FROM config WHERE name=?";
63 $query = $this->executeQuery($sql, array('login'));
64 $login = $query->fetchAll();
65
66 return isset($login[0]['value']) ? $login[0]['value'] : FALSE;
67 }
68
69 public function getPassword() {
70 $sql = "SELECT value FROM config WHERE name=?";
71 $query = $this->executeQuery($sql, array('password'));
72 $pass = $query->fetchAll();
73
74 return isset($pass[0]['value']) ? $pass[0]['value'] : FALSE;
75 }
76
77 public function updatePassword($password)
78 {
79 $sql_update = "UPDATE config SET value=? WHERE name='password'";
80 $params_update = array($password);
81 $query = $this->executeQuery($sql_update, $params_update);
82 }
83
84 private function executeQuery($sql, $params) {
85 try
86 {
87 $query = $this->getHandle()->prepare($sql);
88 $query->execute($params);
89 return $query;
90 }
91 catch (Exception $e)
92 {
93 logm('execute query error : '.$e->getMessage());
94 }
95 }
96
97 public function retrieveAll() {
98 $sql = "SELECT * FROM entries ORDER BY id";
99 $query = $this->executeQuery($sql, array());
100 $entries = $query->fetchAll();
101
102 return $entries;
103 }
104
105 public function retrieveOneById($id) {
106 parent::__construct();
107
108 $entry = NULL;
109 $sql = "SELECT * FROM entries WHERE id=?";
110 $params = array(intval($id));
111 $query = $this->executeQuery($sql, $params);
112 $entry = $query->fetchAll();
113
114 return $entry[0];
115 }
116
117 public function getEntriesByView($view) {
118 parent::__construct();
119
120 switch ($_SESSION['sort'])
121 {
122 case 'ia':
123 $order = 'ORDER BY id';
124 break;
125 case 'id':
126 $order = 'ORDER BY id DESC';
127 break;
128 case 'ta':
129 $order = 'ORDER BY lower(title)';
130 break;
131 case 'td':
132 $order = 'ORDER BY lower(title) DESC';
133 break;
134 default:
135 $order = 'ORDER BY id';
136 break;
137 }
138
139 switch ($view)
140 {
141 case 'archive':
142 $sql = "SELECT * FROM entries WHERE is_read=? " . $order;
143 $params = array(-1);
144 break;
145 case 'fav' :
146 $sql = "SELECT * FROM entries WHERE is_fav=? " . $order;
147 $params = array(-1);
148 break;
149 default:
150 $sql = "SELECT * FROM entries WHERE is_read=? " . $order;
151 $params = array(0);
152 break;
153 }
154
155 $query = $this->executeQuery($sql, $params);
156 $entries = $query->fetchAll();
157
158 return $entries;
159 }
160
161 public function add($url, $title, $content) {
162 parent::__construct();
163 $sql_action = 'INSERT INTO entries ( url, title, content ) VALUES (?, ?, ?)';
164 $params_action = array($url, $title, $content);
165 $query = $this->executeQuery($sql_action, $params_action);
166 return $query;
167 }
168
169 public function deleteById($id) {
170 parent::__construct();
171 $sql_action = "DELETE FROM entries WHERE id=?";
172 $params_action = array($id);
173 $query = $this->executeQuery($sql_action, $params_action);
174 return $query;
175 }
176
177 public function favoriteById($id) {
178 parent::__construct();
179 $sql_action = "UPDATE entries SET is_fav=~is_fav WHERE id=?";
180 $params_action = array($id);
181 $query = $this->executeQuery($sql_action, $params_action);
182 }
183
184 public function archiveById($id) {
185 parent::__construct();
186 $sql_action = "UPDATE entries SET is_read=~is_read WHERE id=?";
187 $params_action = array($id);
188 $query = $this->executeQuery($sql_action, $params_action);
189 }
190
191 public function getLastId() {
192 parent::__construct();
193 return $this->getHandle()->lastInsertId();
194 }
195
196 public function updateContentById($id) {
197 parent::__construct();
198 $sql_update = "UPDATE entries SET content=? WHERE id=?";
199 $params_update = array($content, $id);
200 $query = $this->executeQuery($sql_update, $params_update);
201 }
202}
diff --git a/inc/store/store.class.php b/inc/store/store.class.php
deleted file mode 100644
index dd7d4cfe..00000000
--- a/inc/store/store.class.php
+++ /dev/null
@@ -1,63 +0,0 @@
1<?php
2/**
3 * poche, a read it later open source system
4 *
5 * @category poche
6 * @author Nicolas Lœuillet <support@inthepoche.com>
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Store {
12 function __construct() {
13
14 }
15
16 public function getLogin() {
17
18 }
19
20 public function getPassword() {
21
22 }
23
24 public function add() {
25
26 }
27
28 public function retrieveAll() {
29
30 }
31
32 public function retrieveOneById($id) {
33
34 }
35
36 public function retrieveOneByURL($url) {
37
38 }
39
40 public function deleteById($id) {
41
42 }
43
44 public function favoriteById($id) {
45
46 }
47
48 public function archiveById($id) {
49
50 }
51
52 public function getEntriesByView($view) {
53
54 }
55
56 public function getLastId() {
57
58 }
59
60 public function updateContentById($id) {
61
62 }
63}