diff options
author | Nicolas Lœuillet <nicolas.loeuillet@gmail.com> | 2013-08-07 10:41:26 -0700 |
---|---|---|
committer | Nicolas Lœuillet <nicolas.loeuillet@gmail.com> | 2013-08-07 10:41:26 -0700 |
commit | 01c0e050ad8eca54f115dfa21db99e4f61ab7ca7 (patch) | |
tree | e1bdacb68b3a56644f4525974844dd954d6e3c6b /inc | |
parent | da2c5d6fc33587c775a7d8a738c2c18de41f83b2 (diff) | |
parent | 339d510fda0a43b08981309f7540acedf3a4976c (diff) | |
download | wallabag-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')
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 | ||
39 | class Messages { | 39 | class 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 | */ | ||
9 | class 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 | |||
11 | class 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 | |||
11 | define ('POCHE_VERSION', '0.3'); | ||
12 | |||
13 | if (!is_dir('db/')) { | ||
14 | @mkdir('db/',0705); | ||
15 | } | ||
16 | |||
17 | define ('MODE_DEMO', FALSE); | ||
18 | define ('ABS_PATH', 'assets/'); | ||
19 | define ('CONVERT_LINKS_FOOTNOTES', TRUE); | ||
20 | define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE); | ||
21 | define ('DOWNLOAD_PICTURES', TRUE); | ||
22 | define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); | ||
23 | define ('LANG', 'fr_FR.UTF8'); | ||
24 | |||
25 | putenv("LC_ALL=".LANG); | ||
26 | setlocale(LC_ALL, LANG); | ||
27 | bindtextdomain(LANG, "./locale"); | ||
28 | textdomain(LANG); | ||
29 | |||
30 | $storage_type = 'sqlite'; # sqlite or file | ||
31 | |||
32 | include 'functions.php'; | ||
33 | require_once 'Readability.php'; | ||
34 | require_once 'Encoding.php'; | ||
35 | require_once 'rain.tpl.class.php'; | ||
36 | require_once 'MyTool.class.php'; | ||
37 | require_once 'Session.class.php'; | ||
38 | require_once 'store/store.class.php'; | ||
39 | require_once 'store/sqlite.class.php'; | ||
40 | require_once 'store/file.class.php'; | ||
41 | require_once 'class.messages.php'; | ||
42 | |||
43 | Session::init(); | ||
44 | |||
45 | $store = new $storage_type(); | ||
46 | # initialisation de RainTPL | ||
47 | raintpl::$tpl_dir = './tpl/'; | ||
48 | raintpl::$cache_dir = './cache/'; | ||
49 | raintpl::$base_url = get_poche_url(); | ||
50 | raintpl::configure('path_replace', false); | ||
51 | raintpl::configure('debug', false); | ||
52 | $tpl = new raintpl(); | ||
53 | |||
54 | if(!$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 | */ | ||
14 | function 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 | |||
26 | function encode_string($string) | ||
27 | { | ||
28 | return sha1($string . SALT); | ||
29 | } | ||
30 | |||
31 | // function define to retrieve url content | ||
32 | function 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 | */ | ||
116 | function 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 | */ | ||
163 | function 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 | */ | ||
188 | function 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 | |||
221 | function 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 | */ | ||
236 | function 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 | */ | ||
254 | function 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 | |||
265 | function 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 | */ | ||
335 | function 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 | |||
396 | function 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 | |||
11 | class 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 | |||
11 | class 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 | |||
11 | class 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 | |||
11 | class 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 | |||
11 | class 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 | ||
12 | define ('STORAGE','sqlite'); # postgres, mysql, sqlite | ||
13 | define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite | ||
14 | define ('STORAGE_DB', 'poche'); # only for postgres & mysql | ||
15 | define ('STORAGE_SQLITE', './db/poche.sqlite'); | ||
16 | define ('STORAGE_USER', 'postgres'); # leave blank for sqlite | ||
17 | define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite | ||
18 | |||
19 | define ('POCHE_VERSION', '1.0-beta'); | ||
20 | define ('MODE_DEMO', FALSE); | ||
21 | define ('DEBUG_POCHE', FALSE); | ||
22 | define ('CONVERT_LINKS_FOOTNOTES', FALSE); | ||
23 | define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); | ||
24 | define ('DOWNLOAD_PICTURES', FALSE); | ||
25 | define ('SHARE_TWITTER', TRUE); | ||
26 | define ('SHARE_MAIL', TRUE); | ||
27 | define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); | ||
28 | define ('ABS_PATH', 'assets/'); | ||
29 | define ('TPL', './tpl'); | ||
30 | define ('LOCALE', './locale'); | ||
31 | define ('CACHE', './cache'); | ||
32 | define ('LANG', 'en_EN.UTF8'); | ||
33 | define ('PAGINATION', '10'); | ||
34 | define ('THEME', 'light'); | ||
35 | |||
36 | # /!\ Be careful if you change the lines below /!\ | ||
37 | require_once './inc/poche/User.class.php'; | ||
38 | require_once './inc/poche/Tools.class.php'; | ||
39 | require_once './inc/poche/Url.class.php'; | ||
40 | require_once './inc/3rdparty/class.messages.php'; | ||
41 | require_once './inc/poche/Poche.class.php'; | ||
42 | require_once './inc/3rdparty/Readability.php'; | ||
43 | require_once './inc/3rdparty/Encoding.php'; | ||
44 | require_once './inc/poche/Database.class.php'; | ||
45 | require_once './vendor/autoload.php'; | ||
46 | require_once './inc/3rdparty/simple_html_dom.php'; | ||
47 | require_once './inc/3rdparty/paginator.php'; | ||
48 | require_once './inc/3rdparty/Session.class.php'; | ||
49 | |||
50 | if (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 | */ | ||
14 | function 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 | */ | ||
37 | function 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 | */ | ||
68 | function 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 | */ | ||
83 | function 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 | */ | ||
101 | function 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 | |||
13 | class 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("<?","?>"), $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 | */ | ||
944 | class 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 | */ | ||
976 | class RainTpl_NotFoundException extends RainTpl_Exception{ | ||
977 | } | ||
978 | |||
979 | /** | ||
980 | * Exception thrown when syntax error occurs. | ||
981 | */ | ||
982 | class 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 | |||
11 | class 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 | |||
11 | class 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 | |||
11 | class 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 | } | ||