diff options
author | Nicolas Lœuillet <nicolas.loeuillet@gmail.com> | 2013-09-20 04:36:35 -0700 |
---|---|---|
committer | Nicolas Lœuillet <nicolas.loeuillet@gmail.com> | 2013-09-20 04:36:35 -0700 |
commit | 79026b73a804d1fe3715c3edf5bc2cfb1e56732c (patch) | |
tree | 225136afe844ad0b1c3fef443fcea86fe54384e5 /inc | |
parent | c51be6b697da573cdcf0788eb8617130ce5517a4 (diff) | |
parent | 6a6c1c1172f3c6167d360a2322335ea91e94a3ff (diff) | |
download | wallabag-79026b73a804d1fe3715c3edf5bc2cfb1e56732c.tar.gz wallabag-79026b73a804d1fe3715c3edf5bc2cfb1e56732c.tar.zst wallabag-79026b73a804d1fe3715c3edf5bc2cfb1e56732c.zip |
Merge pull request #226 from inthepoche/dev1.0-beta5
beta5
Diffstat (limited to 'inc')
-rw-r--r-- | inc/3rdparty/FlattrItem.class.php | 49 | ||||
-rw-r--r-- | inc/3rdparty/Session.class.php | 283 | ||||
-rw-r--r--[-rwxr-xr-x] | inc/3rdparty/class.messages.php | 0 | ||||
-rw-r--r-- | inc/3rdparty/content-extractor/ContentExtractor.php | 2 | ||||
-rw-r--r-- | inc/3rdparty/site_config/README.txt | 6 | ||||
-rw-r--r-- | inc/3rdparty/site_config/custom/inthepoche.com.txt | 7 | ||||
-rw-r--r-- | inc/3rdparty/site_config/index.php | 3 | ||||
-rw-r--r-- | inc/3rdparty/site_config/standard/.wikipedia.org.txt | 19 | ||||
-rw-r--r-- | inc/3rdparty/site_config/standard/index.php | 3 | ||||
-rw-r--r-- | inc/3rdparty/site_config/standard/version.php | 2 | ||||
-rw-r--r-- | inc/poche/Database.class.php | 16 | ||||
-rw-r--r-- | inc/poche/Poche.class.php | 335 | ||||
-rw-r--r-- | inc/poche/PocheReadability.php | 46 | ||||
-rw-r--r-- | inc/poche/Tools.class.php | 41 | ||||
-rw-r--r-- | inc/poche/Url.class.php | 2 | ||||
-rwxr-xr-x | inc/poche/config.inc.php | 59 | ||||
-rwxr-xr-x | inc/poche/config.inc.php.new | 56 | ||||
-rw-r--r-- | inc/poche/define.inc.php | 7 | ||||
-rw-r--r-- | inc/poche/global.inc.php | 64 |
19 files changed, 722 insertions, 278 deletions
diff --git a/inc/3rdparty/FlattrItem.class.php b/inc/3rdparty/FlattrItem.class.php new file mode 100644 index 00000000..c940fcd6 --- /dev/null +++ b/inc/3rdparty/FlattrItem.class.php | |||
@@ -0,0 +1,49 @@ | |||
1 | <?php | ||
2 | /* | ||
3 | * Class for Flattr querying | ||
4 | */ | ||
5 | class FlattrItem { | ||
6 | |||
7 | public $status; | ||
8 | public $urltoflattr; | ||
9 | public $flattrItemURL; | ||
10 | public $numflattrs; | ||
11 | |||
12 | public function checkItem($urltoflattr) { | ||
13 | $this->cacheflattrfile($urltoflattr); | ||
14 | $flattrResponse = file_get_contents(CACHE . "/flattr/".base64_encode($urltoflattr).".cache"); | ||
15 | if($flattrResponse != FALSE) { | ||
16 | $result = json_decode($flattrResponse); | ||
17 | if (isset($result->message)){ | ||
18 | if ($result->message == "flattrable") { | ||
19 | $this->status = FLATTRABLE; | ||
20 | } | ||
21 | } | ||
22 | elseif ($result->link) { | ||
23 | $this->status = FLATTRED; | ||
24 | $this->flattrItemURL = $result->link; | ||
25 | $this->numflattrs = $result->flattrs; | ||
26 | } | ||
27 | else { | ||
28 | $this->status = NOT_FLATTRABLE; | ||
29 | } | ||
30 | } | ||
31 | else { | ||
32 | $this->status = "FLATTR_ERR_CONNECTION"; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | private function cacheflattrfile($urltoflattr) { | ||
37 | if (!is_dir(CACHE . '/flattr')) { | ||
38 | mkdir(CACHE . '/flattr', 0777); | ||
39 | } | ||
40 | |||
41 | // if a cache flattr file for this url already exists and it's been less than one day than it have been updated, see in /cache | ||
42 | if ((!file_exists(CACHE . "/flattr/".base64_encode($urltoflattr).".cache")) || (time() - filemtime(CACHE . "/flattr/".base64_encode($urltoflattr).".cache") > 86400)) { | ||
43 | $askForFlattr = Tools::getFile(FLATTR_API . $urltoflattr); | ||
44 | $flattrCacheFile = fopen(CACHE . "/flattr/".base64_encode($urltoflattr).".cache", 'w+'); | ||
45 | fwrite($flattrCacheFile, $askForFlattr); | ||
46 | fclose($flattrCacheFile); | ||
47 | } | ||
48 | } | ||
49 | } \ No newline at end of file | ||
diff --git a/inc/3rdparty/Session.class.php b/inc/3rdparty/Session.class.php index 3162f507..df913a06 100644 --- a/inc/3rdparty/Session.class.php +++ b/inc/3rdparty/Session.class.php | |||
@@ -1,136 +1,279 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | 2 | /** |
3 | * Session management class | 3 | * Session management class |
4 | * | ||
4 | * http://www.developpez.net/forums/d51943/php/langage/sessions/ | 5 | * http://www.developpez.net/forums/d51943/php/langage/sessions/ |
5 | * http://sebsauvage.net/wiki/doku.php?id=php:session | 6 | * http://sebsauvage.net/wiki/doku.php?id=php:session |
6 | * http://sebsauvage.net/wiki/doku.php?id=php:shaarli | 7 | * http://sebsauvage.net/wiki/doku.php?id=php:shaarli |
7 | * | 8 | * |
8 | * Features: | 9 | * Features: |
9 | * - Everything is stored on server-side (we do not trust client-side data, | 10 | * - Everything is stored on server-side (we do not trust client-side data, |
10 | * such as cookie expiration) | 11 | * such as cookie expiration) |
11 | * - IP addresses + user agent are checked on each access to prevent session | 12 | * - IP addresses are checked on each access to prevent session cookie hijacking |
12 | * cookie hijacking (such as Firesheep) | 13 | * (such as Firesheep) |
13 | * - Session expires on user inactivity (Session expiration date is | 14 | * - Session expires on user inactivity (Session expiration date is |
14 | * automatically updated everytime the user accesses a page.) | 15 | * automatically updated everytime the user accesses a page.) |
15 | * - A unique secret key is generated on server-side for this session | 16 | * - A unique secret key is generated on server-side for this session |
16 | * (and never sent over the wire) which can be used | 17 | * (and never sent over the wire) which can be used to sign forms (HMAC) |
17 | * to sign forms (HMAC) (See $_SESSION['uid'] ) | 18 | * (See $_SESSION['uid']) |
18 | * - Token management to prevent XSRF attacks. | 19 | * - Token management to prevent XSRF attacks |
20 | * - Brute force protection with ban management | ||
19 | * | 21 | * |
20 | * TODO: | 22 | * TODOs |
21 | * - log login fail | 23 | * - Replace globals with variables in Session class |
22 | * - prevent brute force (ban IP) | ||
23 | * | 24 | * |
24 | * HOWTOUSE: | 25 | * How to use: |
25 | * - Just call Session::init(); to initialize session and | 26 | * - http://tontof.net/kriss/php5/session |
26 | * check if connected with Session::isLogged() | ||
27 | */ | 27 | */ |
28 | |||
29 | class Session | 28 | class Session |
30 | { | 29 | { |
30 | // Personnalize PHP session name | ||
31 | public static $sessionName = ''; | ||
31 | // If the user does not access any page within this time, | 32 | // If the user does not access any page within this time, |
32 | // his/her session is considered expired (in seconds). | 33 | // his/her session is considered expired (3600 sec. = 1 hour) |
33 | public static $inactivity_timeout = 3600; | 34 | public static $inactivityTimeout = 3600; |
34 | private static $_instance; | 35 | // If you get disconnected often or if your IP address changes often. |
36 | // Let you disable session cookie hijacking protection | ||
37 | public static $disableSessionProtection = false; | ||
38 | // Ban IP after this many failures. | ||
39 | public static $banAfter = 4; | ||
40 | // Ban duration for IP address after login failures (in seconds). | ||
41 | // (1800 sec. = 30 minutes) | ||
42 | public static $banDuration = 1800; | ||
43 | // File storage for failures and bans. If empty, no ban management. | ||
44 | public static $banFile = ''; | ||
35 | 45 | ||
36 | // constructor | 46 | /** |
37 | private function __construct() | 47 | * Initialize session |
48 | */ | ||
49 | public static function init() | ||
38 | { | 50 | { |
51 | // Force cookie path (but do not change lifetime) | ||
52 | $cookie = session_get_cookie_params(); | ||
53 | // Default cookie expiration and path. | ||
54 | $cookiedir = ''; | ||
55 | if (dirname($_SERVER['SCRIPT_NAME'])!='/') { | ||
56 | $cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/'; | ||
57 | } | ||
58 | $ssl = false; | ||
59 | if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") { | ||
60 | $ssl = true; | ||
61 | } | ||
62 | session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['HTTP_HOST'], $ssl); | ||
39 | // Use cookies to store session. | 63 | // Use cookies to store session. |
40 | ini_set('session.use_cookies', 1); | 64 | ini_set('session.use_cookies', 1); |
41 | // Force cookies for session (phpsessionID forbidden in URL) | 65 | // Force cookies for session (phpsessionID forbidden in URL) |
42 | ini_set('session.use_only_cookies', 1); | 66 | ini_set('session.use_only_cookies', 1); |
43 | if (!session_id()){ | 67 | if (!session_id()) { |
44 | // Prevent php to use sessionID in URL if cookies are disabled. | 68 | // Prevent php to use sessionID in URL if cookies are disabled. |
45 | ini_set('session.use_trans_sid', false); | 69 | ini_set('session.use_trans_sid', false); |
46 | session_start('poche'); | 70 | if (!empty(self::$sessionName)) { |
71 | session_name(self::$sessionName); | ||
72 | } | ||
73 | session_start(); | ||
47 | } | 74 | } |
48 | } | 75 | } |
49 | 76 | ||
50 | // initialize session | 77 | /** |
51 | public static function init() | 78 | * Returns the IP address |
79 | * (Used to prevent session cookie hijacking.) | ||
80 | * | ||
81 | * @return string IP addresses | ||
82 | */ | ||
83 | private static function _allIPs() | ||
52 | { | 84 | { |
53 | if (!isset(self::$_instance)) { | 85 | $ip = $_SERVER["REMOTE_ADDR"]; |
54 | self::$_instance = new Session(); | 86 | $ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : ''; |
55 | } | 87 | $ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : ''; |
56 | } | ||
57 | 88 | ||
58 | // Returns the IP address, user agent and language of the client | 89 | return $ip; |
59 | // (Used to prevent session cookie hijacking.) | ||
60 | private static function _allInfos() | ||
61 | { | ||
62 | $infos = $_SERVER["REMOTE_ADDR"]; | ||
63 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { | ||
64 | $infos.=$_SERVER['HTTP_X_FORWARDED_FOR']; | ||
65 | } | ||
66 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { | ||
67 | $infos.='_'.$_SERVER['HTTP_CLIENT_IP']; | ||
68 | } | ||
69 | $infos.='_'.$_SERVER['HTTP_USER_AGENT']; | ||
70 | $infos.='_'.$_SERVER['HTTP_ACCEPT_LANGUAGE']; | ||
71 | return sha1($infos); | ||
72 | } | 90 | } |
73 | 91 | ||
74 | // Check that user/password is correct and init some SESSION variables. | 92 | /** |
75 | public static function login($login,$password,$login_test,$password_test, | 93 | * Check that user/password is correct and then init some SESSION variables. |
76 | $pValues = array()) | 94 | * |
95 | * @param string $login Login reference | ||
96 | * @param string $password Password reference | ||
97 | * @param string $loginTest Login to compare with login reference | ||
98 | * @param string $passwordTest Password to compare with password reference | ||
99 | * @param array $pValues Array of variables to store in SESSION | ||
100 | * | ||
101 | * @return true|false True if login and password are correct, false | ||
102 | * otherwise | ||
103 | */ | ||
104 | public static function login ( | ||
105 | $login, | ||
106 | $password, | ||
107 | $loginTest, | ||
108 | $passwordTest, | ||
109 | $pValues = array()) | ||
77 | { | 110 | { |
78 | foreach ($pValues as $key => $value) { | 111 | self::banInit(); |
79 | $_SESSION[$key] = $value; | 112 | if (self::banCanLogin()) { |
80 | } | 113 | if ($login === $loginTest && $password === $passwordTest) { |
81 | if ($login==$login_test && $password==$password_test){ | 114 | self::banLoginOk(); |
82 | // generate unique random number to sign forms (HMAC) | 115 | // Generate unique random number to sign forms (HMAC) |
83 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); | 116 | $_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand()); |
84 | $_SESSION['info']=Session::_allInfos(); | 117 | $_SESSION['ip'] = self::_allIPs(); |
85 | $_SESSION['username']=$login; | 118 | $_SESSION['username'] = $login; |
86 | // Set session expiration. | 119 | // Set session expiration. |
87 | $_SESSION['expires_on']=time()+Session::$inactivity_timeout; | 120 | $_SESSION['expires_on'] = time() + self::$inactivityTimeout; |
88 | return true; | 121 | |
122 | foreach ($pValues as $key => $value) { | ||
123 | $_SESSION[$key] = $value; | ||
124 | } | ||
125 | |||
126 | return true; | ||
127 | } | ||
128 | self::banLoginFailed(); | ||
89 | } | 129 | } |
130 | |||
90 | return false; | 131 | return false; |
91 | } | 132 | } |
92 | 133 | ||
93 | // Force logout | 134 | /** |
135 | * Unset SESSION variable to force logout | ||
136 | */ | ||
94 | public static function logout() | 137 | public static function logout() |
95 | { | 138 | { |
96 | unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']); | 139 | unset($_SESSION['uid'],$_SESSION['ip'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']); |
97 | } | 140 | } |
98 | 141 | ||
99 | // Make sure user is logged in. | 142 | /** |
143 | * Make sure user is logged in. | ||
144 | * | ||
145 | * @return true|false True if user is logged in, false otherwise | ||
146 | */ | ||
100 | public static function isLogged() | 147 | public static function isLogged() |
101 | { | 148 | { |
102 | if (!isset ($_SESSION['uid']) | 149 | if (!isset ($_SESSION['uid']) |
103 | || $_SESSION['info']!=Session::_allInfos() | 150 | || (self::$disableSessionProtection === false |
104 | || time()>=$_SESSION['expires_on']){ | 151 | && $_SESSION['ip'] !== self::_allIPs()) |
105 | Session::logout(); | 152 | || time() >= $_SESSION['expires_on']) { |
153 | self::logout(); | ||
154 | |||
106 | return false; | 155 | return false; |
107 | } | 156 | } |
108 | // User accessed a page : Update his/her session expiration date. | 157 | // User accessed a page : Update his/her session expiration date. |
109 | $_SESSION['expires_on']=time()+Session::$inactivity_timeout; | 158 | $_SESSION['expires_on'] = time() + self::$inactivityTimeout; |
159 | if (!empty($_SESSION['longlastingsession'])) { | ||
160 | $_SESSION['expires_on'] += $_SESSION['longlastingsession']; | ||
161 | } | ||
162 | |||
110 | return true; | 163 | return true; |
111 | } | 164 | } |
112 | 165 | ||
113 | // Returns a token. | 166 | /** |
114 | public static function getToken() | 167 | * Create a token, store it in SESSION and return it |
168 | * | ||
169 | * @param string $salt to prevent birthday attack | ||
170 | * | ||
171 | * @return string Token created | ||
172 | */ | ||
173 | public static function getToken($salt = '') | ||
115 | { | 174 | { |
116 | if (!isset($_SESSION['tokens'])){ | 175 | if (!isset($_SESSION['tokens'])) { |
117 | $_SESSION['tokens']=array(); | 176 | $_SESSION['tokens']=array(); |
118 | } | 177 | } |
119 | // We generate a random string and store it on the server side. | 178 | // We generate a random string and store it on the server side. |
120 | $rnd = sha1(uniqid('',true).'_'.mt_rand()); | 179 | $rnd = sha1(uniqid('', true).'_'.mt_rand().$salt); |
121 | $_SESSION['tokens'][$rnd]=1; | 180 | $_SESSION['tokens'][$rnd]=1; |
181 | |||
122 | return $rnd; | 182 | return $rnd; |
123 | } | 183 | } |
124 | 184 | ||
125 | // Tells if a token is ok. Using this function will destroy the token. | 185 | /** |
126 | // return true if token is ok. | 186 | * Tells if a token is ok. Using this function will destroy the token. |
187 | * | ||
188 | * @param string $token Token to test | ||
189 | * | ||
190 | * @return true|false True if token is correct, false otherwise | ||
191 | */ | ||
127 | public static function isToken($token) | 192 | public static function isToken($token) |
128 | { | 193 | { |
129 | if (isset($_SESSION['tokens'][$token])) | 194 | if (isset($_SESSION['tokens'][$token])) { |
130 | { | ||
131 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. | 195 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. |
196 | |||
132 | return true; // Token is ok. | 197 | return true; // Token is ok. |
133 | } | 198 | } |
199 | |||
134 | return false; // Wrong token, or already used. | 200 | return false; // Wrong token, or already used. |
135 | } | 201 | } |
136 | } \ No newline at end of file | 202 | |
203 | /** | ||
204 | * Signal a failed login. Will ban the IP if too many failures: | ||
205 | */ | ||
206 | public static function banLoginFailed() | ||
207 | { | ||
208 | if (self::$banFile !== '') { | ||
209 | $ip = $_SERVER["REMOTE_ADDR"]; | ||
210 | $gb = $GLOBALS['IPBANS']; | ||
211 | |||
212 | if (!isset($gb['FAILURES'][$ip])) { | ||
213 | $gb['FAILURES'][$ip] = 0; | ||
214 | } | ||
215 | $gb['FAILURES'][$ip]++; | ||
216 | if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) { | ||
217 | $gb['BANS'][$ip]= time() + self::$banDuration; | ||
218 | } | ||
219 | |||
220 | $GLOBALS['IPBANS'] = $gb; | ||
221 | file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>"); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * Signals a successful login. Resets failed login counter. | ||
227 | */ | ||
228 | public static function banLoginOk() | ||
229 | { | ||
230 | if (self::$banFile !== '') { | ||
231 | $ip = $_SERVER["REMOTE_ADDR"]; | ||
232 | $gb = $GLOBALS['IPBANS']; | ||
233 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); | ||
234 | $GLOBALS['IPBANS'] = $gb; | ||
235 | file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>"); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * Ban init | ||
241 | */ | ||
242 | public static function banInit() | ||
243 | { | ||
244 | if (self::$banFile !== '') { | ||
245 | if (!is_file(self::$banFile)) { | ||
246 | file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(), 'BANS'=>array()), true).";\n?>"); | ||
247 | } | ||
248 | include self::$banFile; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * Checks if the user CAN login. If 'true', the user can try to login. | ||
254 | * | ||
255 | * @return boolean true if user is banned, false otherwise | ||
256 | */ | ||
257 | public static function banCanLogin() | ||
258 | { | ||
259 | if (self::$banFile !== '') { | ||
260 | $ip = $_SERVER["REMOTE_ADDR"]; | ||
261 | $gb = $GLOBALS['IPBANS']; | ||
262 | if (isset($gb['BANS'][$ip])) { | ||
263 | // User is banned. Check if the ban has expired: | ||
264 | if ($gb['BANS'][$ip] <= time()) { | ||
265 | // Ban expired, user can try to login again. | ||
266 | unset($gb['FAILURES'][$ip]); | ||
267 | unset($gb['BANS'][$ip]); | ||
268 | file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>"); | ||
269 | |||
270 | return true; // Ban has expired, user can login. | ||
271 | } | ||
272 | |||
273 | return false; // User is banned. | ||
274 | } | ||
275 | } | ||
276 | |||
277 | return true; // User is not banned. | ||
278 | } | ||
279 | } | ||
diff --git a/inc/3rdparty/class.messages.php b/inc/3rdparty/class.messages.php index e60bd3a1..e60bd3a1 100755..100644 --- a/inc/3rdparty/class.messages.php +++ b/inc/3rdparty/class.messages.php | |||
diff --git a/inc/3rdparty/content-extractor/ContentExtractor.php b/inc/3rdparty/content-extractor/ContentExtractor.php index db371c6a..26878392 100644 --- a/inc/3rdparty/content-extractor/ContentExtractor.php +++ b/inc/3rdparty/content-extractor/ContentExtractor.php | |||
@@ -138,7 +138,7 @@ class ContentExtractor | |||
138 | } | 138 | } |
139 | 139 | ||
140 | // load and parse html | 140 | // load and parse html |
141 | $this->readability = new Readability($html, $url); | 141 | $this->readability = new PocheReadability($html, $url); |
142 | 142 | ||
143 | // we use xpath to find elements in the given HTML document | 143 | // we use xpath to find elements in the given HTML document |
144 | // see http://en.wikipedia.org/wiki/XPath_1.0 | 144 | // see http://en.wikipedia.org/wiki/XPath_1.0 |
diff --git a/inc/3rdparty/site_config/README.txt b/inc/3rdparty/site_config/README.txt deleted file mode 100644 index 0aff456b..00000000 --- a/inc/3rdparty/site_config/README.txt +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | Full-Text RSS Site Patterns | ||
2 | --------------------------- | ||
3 | |||
4 | Site patterns allow you to specify what should be extracted from specific sites. | ||
5 | |||
6 | Please see http://help.fivefilters.org/customer/portal/articles/223153-site-patterns for more information. \ No newline at end of file | ||
diff --git a/inc/3rdparty/site_config/custom/inthepoche.com.txt b/inc/3rdparty/site_config/custom/inthepoche.com.txt deleted file mode 100644 index ede74b97..00000000 --- a/inc/3rdparty/site_config/custom/inthepoche.com.txt +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | title: //title | ||
2 | body: //div[@class='post-content'] | ||
3 | |||
4 | prune: no | ||
5 | tidy: no | ||
6 | |||
7 | test_url: http://www.inthepoche.com/?post/poche-hosting \ No newline at end of file | ||
diff --git a/inc/3rdparty/site_config/index.php b/inc/3rdparty/site_config/index.php deleted file mode 100644 index a3d5f739..00000000 --- a/inc/3rdparty/site_config/index.php +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | <?php | ||
2 | // this is here to prevent directory listing over the web | ||
3 | ?> \ No newline at end of file | ||
diff --git a/inc/3rdparty/site_config/standard/.wikipedia.org.txt b/inc/3rdparty/site_config/standard/.wikipedia.org.txt deleted file mode 100644 index 8b98ae4b..00000000 --- a/inc/3rdparty/site_config/standard/.wikipedia.org.txt +++ /dev/null | |||
@@ -1,19 +0,0 @@ | |||
1 | title: //h1[@id='firstHeading'] | ||
2 | body: //div[@id = 'bodyContent'] | ||
3 | strip_id_or_class: editsection | ||
4 | #strip_id_or_class: toc | ||
5 | strip_id_or_class: vertical-navbox | ||
6 | strip: //table[@id='toc'] | ||
7 | strip: //div[@id='catlinks'] | ||
8 | strip: //div[@id='jump-to-nav'] | ||
9 | strip: //div[@class='thumbcaption']//div[@class='magnify'] | ||
10 | strip: //table[@class='navbox'] | ||
11 | strip: //table[contains(@class, 'infobox')] | ||
12 | strip: //div[@class='dablink'] | ||
13 | strip: //div[@id='contentSub'] | ||
14 | strip: //table[contains(@class, 'metadata')] | ||
15 | strip: //*[contains(@class, 'noprint')] | ||
16 | strip: //span[@title='pronunciation:'] | ||
17 | prune: no | ||
18 | tidy: no | ||
19 | test_url: http://en.wikipedia.org/wiki/Christopher_Lloyd \ No newline at end of file | ||
diff --git a/inc/3rdparty/site_config/standard/index.php b/inc/3rdparty/site_config/standard/index.php deleted file mode 100644 index a3d5f739..00000000 --- a/inc/3rdparty/site_config/standard/index.php +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | <?php | ||
2 | // this is here to prevent directory listing over the web | ||
3 | ?> \ No newline at end of file | ||
diff --git a/inc/3rdparty/site_config/standard/version.php b/inc/3rdparty/site_config/standard/version.php deleted file mode 100644 index e61807ed..00000000 --- a/inc/3rdparty/site_config/standard/version.php +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | <?php | ||
2 | return 1; \ No newline at end of file | ||
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php index 84916b83..4d664992 100644 --- a/inc/poche/Database.class.php +++ b/inc/poche/Database.class.php | |||
@@ -60,11 +60,15 @@ class Database { | |||
60 | $id_user = intval($this->getLastId($sequence)); | 60 | $id_user = intval($this->getLastId($sequence)); |
61 | 61 | ||
62 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | 62 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; |
63 | $params = array($id_user, 'pager', '10'); | 63 | $params = array($id_user, 'pager', PAGINATION); |
64 | $query = $this->executeQuery($sql, $params); | 64 | $query = $this->executeQuery($sql, $params); |
65 | 65 | ||
66 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | 66 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; |
67 | $params = array($id_user, 'language', 'en_EN.UTF8'); | 67 | $params = array($id_user, 'language', LANG); |
68 | $query = $this->executeQuery($sql, $params); | ||
69 | |||
70 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | ||
71 | $params = array($id_user, 'theme', DEFAULT_THEME); | ||
68 | $query = $this->executeQuery($sql, $params); | 72 | $query = $this->executeQuery($sql, $params); |
69 | 73 | ||
70 | return TRUE; | 74 | return TRUE; |
@@ -101,10 +105,16 @@ class Database { | |||
101 | return $user; | 105 | return $user; |
102 | } | 106 | } |
103 | 107 | ||
104 | public function updatePassword($id, $password) | 108 | public function updatePassword($userId, $password) |
105 | { | 109 | { |
106 | $sql_update = "UPDATE users SET password=? WHERE id=?"; | 110 | $sql_update = "UPDATE users SET password=? WHERE id=?"; |
107 | $params_update = array($password, $id); | 111 | $params_update = array($password, $id); |
112 | $this->updateUserConfig($userId, 'password', $password); | ||
113 | } | ||
114 | |||
115 | public function updateUserConfig($userId, $key, $value) { | ||
116 | $sql_update = "UPDATE users_config SET `value`=? WHERE `user_id`=? AND `name`=?"; | ||
117 | $params_update = array($value, $userId, $key); | ||
108 | $query = $this->executeQuery($sql_update, $params_update); | 118 | $query = $this->executeQuery($sql_update, $params_update); |
109 | } | 119 | } |
110 | 120 | ||
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 646193f7..18860ddc 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php | |||
@@ -10,77 +10,200 @@ | |||
10 | 10 | ||
11 | class Poche | 11 | class Poche |
12 | { | 12 | { |
13 | public static $canRenderTemplates = true; | ||
14 | public static $configFileAvailable = true; | ||
15 | |||
13 | public $user; | 16 | public $user; |
14 | public $store; | 17 | public $store; |
15 | public $tpl; | 18 | public $tpl; |
16 | public $messages; | 19 | public $messages; |
17 | public $pagination; | 20 | public $pagination; |
18 | 21 | ||
19 | function __construct() | 22 | private $currentTheme = ''; |
23 | private $notInstalledMessage = ''; | ||
24 | |||
25 | # @todo make this dynamic (actually install themes and save them in the database including author information et cetera) | ||
26 | private $installedThemes = array( | ||
27 | 'default' => array('requires' => array()), | ||
28 | 'dark' => array('requires' => array('default')), | ||
29 | 'dmagenta' => array('requires' => array('default')), | ||
30 | 'solarized' => array('requires' => array('default')), | ||
31 | 'solarized-dark' => array('requires' => array('default')) | ||
32 | ); | ||
33 | |||
34 | public function __construct() | ||
20 | { | 35 | { |
36 | if (! $this->configFileIsAvailable()) { | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | $this->init(); | ||
41 | |||
42 | if (! $this->themeIsInstalled()) { | ||
43 | return; | ||
44 | } | ||
45 | |||
21 | $this->initTpl(); | 46 | $this->initTpl(); |
22 | if (!$this->checkBeforeInstall()) { | 47 | |
23 | exit; | 48 | if (! $this->systemIsInstalled()) { |
49 | return; | ||
24 | } | 50 | } |
51 | |||
25 | $this->store = new Database(); | 52 | $this->store = new Database(); |
26 | $this->init(); | ||
27 | $this->messages = new Messages(); | 53 | $this->messages = new Messages(); |
28 | 54 | ||
29 | # installation | 55 | # installation |
30 | if(!$this->store->isInstalled()) | 56 | if (! $this->store->isInstalled()) { |
31 | { | ||
32 | $this->install(); | 57 | $this->install(); |
33 | } | 58 | } |
34 | } | 59 | } |
60 | |||
61 | private function init() | ||
62 | { | ||
63 | Tools::initPhp(); | ||
64 | Session::$sessionName = 'poche'; | ||
65 | Session::init(); | ||
35 | 66 | ||
67 | if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) { | ||
68 | $this->user = $_SESSION['poche_user']; | ||
69 | } else { | ||
70 | # fake user, just for install & login screens | ||
71 | $this->user = new User(); | ||
72 | $this->user->setConfig($this->getDefaultConfig()); | ||
73 | } | ||
74 | |||
75 | # l10n | ||
76 | $language = $this->user->getConfigValue('language'); | ||
77 | putenv('LC_ALL=' . $language); | ||
78 | setlocale(LC_ALL, $language); | ||
79 | bindtextdomain($language, LOCALE); | ||
80 | textdomain($language); | ||
81 | |||
82 | # Pagination | ||
83 | $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); | ||
84 | |||
85 | # Set up theme | ||
86 | $themeDirectory = $this->user->getConfigValue('theme'); | ||
87 | |||
88 | if ($themeDirectory === false) { | ||
89 | $themeDirectory = DEFAULT_THEME; | ||
90 | } | ||
91 | |||
92 | $this->currentTheme = $themeDirectory; | ||
93 | } | ||
94 | |||
95 | public function configFileIsAvailable() { | ||
96 | if (! self::$configFileAvailable) { | ||
97 | $this->notInstalledMessage = 'You have to rename <strong>inc/poche/config.inc.php.new</strong> to <strong>inc/poche/config.inc.php</strong>.'; | ||
98 | |||
99 | return false; | ||
100 | } | ||
101 | |||
102 | return true; | ||
103 | } | ||
104 | |||
105 | public function themeIsInstalled() { | ||
106 | # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet | ||
107 | if (! self::$canRenderTemplates) { | ||
108 | $this->notInstalledMessage = 'Twig does not seem to be installed. Please initialize the Composer installation to automatically fetch dependencies. Have a look at <a href="http://inthepoche.com/?pages/Documentation">the documentation.</a>'; | ||
109 | |||
110 | return false; | ||
111 | } | ||
112 | |||
113 | # Check if the selected theme and its requirements are present | ||
114 | if (! is_dir(THEME . '/' . $this->getTheme())) { | ||
115 | $this->notInstalledMessage = 'The currently selected theme (' . $this->getTheme() . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $this->getTheme() . ')'; | ||
116 | |||
117 | self::$canRenderTemplates = false; | ||
118 | |||
119 | return false; | ||
120 | } | ||
121 | |||
122 | foreach ($this->installedThemes[$this->getTheme()]['requires'] as $requiredTheme) { | ||
123 | if (! is_dir(THEME . '/' . $requiredTheme)) { | ||
124 | $this->notInstalledMessage = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $this->getTheme() . ')'; | ||
125 | |||
126 | self::$canRenderTemplates = false; | ||
127 | |||
128 | return false; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | return true; | ||
133 | } | ||
134 | |||
36 | /** | 135 | /** |
37 | * all checks before installation. | 136 | * all checks before installation. |
137 | * @todo move HTML to template | ||
38 | * @return boolean | 138 | * @return boolean |
39 | */ | 139 | */ |
40 | private function checkBeforeInstall() | 140 | public function systemIsInstalled() |
41 | { | 141 | { |
42 | $msg = ''; | 142 | $msg = ''; |
43 | $allIsGood = TRUE; | 143 | |
44 | 144 | $configSalt = defined('SALT') ? constant('SALT') : ''; | |
45 | if (!is_writable(CACHE)) { | 145 | |
146 | if (empty($configSalt)) { | ||
147 | $msg = '<h1>error</h1><p>You have not yet filled in the SALT value in the config.inc.php file.</p>'; | ||
148 | } else if (! is_writable(CACHE)) { | ||
46 | Tools::logm('you don\'t have write access on cache directory'); | 149 | Tools::logm('you don\'t have write access on cache directory'); |
47 | die('You don\'t have write access on cache directory.'); | 150 | $msg = '<h1>error</h1><p>You don\'t have write access on cache directory.</p>'; |
48 | } | 151 | } else if (STORAGE == 'sqlite' && ! file_exists(STORAGE_SQLITE)) { |
49 | else if (file_exists('./install/update.php') && !DEBUG_POCHE) { | 152 | Tools::logm('sqlite file doesn\'t exist'); |
153 | $msg = '<h1>error</h1><p>sqlite file doesn\'t exist, you can find it in install folder. Copy it in /db folder.</p>'; | ||
154 | } else if (file_exists(ROOT . '/install/update.php') && ! DEBUG_POCHE) { | ||
50 | $msg = '<h1>setup</h1><p><strong>It\'s your first time here?</strong> Please copy /install/poche.sqlite in db folder. Then, delete install folder.<br /><strong>If you have already installed poche</strong>, an update is needed <a href="install/update.php">by clicking here</a>.</p>'; | 155 | $msg = '<h1>setup</h1><p><strong>It\'s your first time here?</strong> Please copy /install/poche.sqlite in db folder. Then, delete install folder.<br /><strong>If you have already installed poche</strong>, an update is needed <a href="install/update.php">by clicking here</a>.</p>'; |
51 | $allIsGood = FALSE; | 156 | } else if (is_dir(ROOT . '/install') && ! DEBUG_POCHE) { |
52 | } | ||
53 | else if (file_exists('./install') && !DEBUG_POCHE) { | ||
54 | $msg = '<h1>setup</h1><p><strong>If you want to update your poche</strong>, you just have to delete /install folder. <br /><strong>To install your poche with sqlite</strong>, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.</p>'; | 157 | $msg = '<h1>setup</h1><p><strong>If you want to update your poche</strong>, you just have to delete /install folder. <br /><strong>To install your poche with sqlite</strong>, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.</p>'; |
55 | $allIsGood = FALSE; | 158 | } else if (STORAGE == 'sqlite' && ! is_writable(STORAGE_SQLITE)) { |
56 | } | ||
57 | else if (STORAGE == 'sqlite' && !is_writable(STORAGE_SQLITE)) { | ||
58 | Tools::logm('you don\'t have write access on sqlite file'); | 159 | Tools::logm('you don\'t have write access on sqlite file'); |
59 | $msg = '<h1>error</h1><p>You don\'t have write access on sqlite file.</p>'; | 160 | $msg = '<h1>error</h1><p>You don\'t have write access on sqlite file.</p>'; |
60 | $allIsGood = FALSE; | ||
61 | } | 161 | } |
62 | 162 | ||
63 | if (!$allIsGood) { | 163 | if (! empty($msg)) { |
64 | echo $this->tpl->render('error.twig', array( | 164 | $this->notInstalledMessage = $msg; |
65 | 'msg' => $msg | 165 | |
66 | )); | 166 | return false; |
67 | } | 167 | } |
68 | 168 | ||
69 | return $allIsGood; | 169 | return true; |
170 | } | ||
171 | |||
172 | public function getNotInstalledMessage() { | ||
173 | return $this->notInstalledMessage; | ||
70 | } | 174 | } |
71 | 175 | ||
72 | private function initTpl() | 176 | private function initTpl() |
73 | { | 177 | { |
74 | # template engine | 178 | $loaderChain = new Twig_Loader_Chain(); |
75 | $loader = new Twig_Loader_Filesystem(TPL); | 179 | |
180 | # add the current theme as first to the loader chain so Twig will look there first for overridden template files | ||
181 | try { | ||
182 | $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $this->getTheme())); | ||
183 | } catch (Twig_Error_Loader $e) { | ||
184 | # @todo isInstalled() should catch this, inject Twig later | ||
185 | die('The currently selected theme (' . $this->getTheme() . ') does not seem to be properly installed (' . THEME . '/' . $this->getTheme() .' is missing)'); | ||
186 | } | ||
187 | |||
188 | # add all required themes to the loader chain | ||
189 | foreach ($this->installedThemes[$this->getTheme()]['requires'] as $requiredTheme) { | ||
190 | try { | ||
191 | $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . DEFAULT_THEME)); | ||
192 | } catch (Twig_Error_Loader $e) { | ||
193 | # @todo isInstalled() should catch this, inject Twig later | ||
194 | die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $this->getTheme() . ')'); | ||
195 | } | ||
196 | } | ||
197 | |||
76 | if (DEBUG_POCHE) { | 198 | if (DEBUG_POCHE) { |
77 | $twig_params = array(); | 199 | $twig_params = array(); |
78 | } | 200 | } else { |
79 | else { | ||
80 | $twig_params = array('cache' => CACHE); | 201 | $twig_params = array('cache' => CACHE); |
81 | } | 202 | } |
82 | $this->tpl = new Twig_Environment($loader, $twig_params); | 203 | |
204 | $this->tpl = new Twig_Environment($loaderChain, $twig_params); | ||
83 | $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); | 205 | $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); |
206 | |||
84 | # filter to display domain name of an url | 207 | # filter to display domain name of an url |
85 | $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); | 208 | $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); |
86 | $this->tpl->addFilter($filter); | 209 | $this->tpl->addFilter($filter); |
@@ -88,38 +211,19 @@ class Poche | |||
88 | # filter for reading time | 211 | # filter for reading time |
89 | $filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime'); | 212 | $filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime'); |
90 | $this->tpl->addFilter($filter); | 213 | $this->tpl->addFilter($filter); |
91 | } | 214 | |
92 | 215 | # filter for simple filenames in config view | |
93 | private function init() | 216 | $filter = new Twig_SimpleFilter('getPrettyFilename', function($string) { return str_replace(ROOT, '', $string); }); |
94 | { | 217 | $this->tpl->addFilter($filter); |
95 | Tools::initPhp(); | ||
96 | Session::init(); | ||
97 | |||
98 | if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) { | ||
99 | $this->user = $_SESSION['poche_user']; | ||
100 | } | ||
101 | else { | ||
102 | # fake user, just for install & login screens | ||
103 | $this->user = new User(); | ||
104 | $this->user->setConfig($this->getDefaultConfig()); | ||
105 | } | ||
106 | |||
107 | # l10n | ||
108 | $language = $this->user->getConfigValue('language'); | ||
109 | putenv('LC_ALL=' . $language); | ||
110 | setlocale(LC_ALL, $language); | ||
111 | bindtextdomain($language, LOCALE); | ||
112 | textdomain($language); | ||
113 | |||
114 | # Pagination | ||
115 | $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); | ||
116 | } | 218 | } |
117 | 219 | ||
118 | private function install() | 220 | private function install() |
119 | { | 221 | { |
120 | Tools::logm('poche still not installed'); | 222 | Tools::logm('poche still not installed'); |
121 | echo $this->tpl->render('install.twig', array( | 223 | echo $this->tpl->render('install.twig', array( |
122 | 'token' => Session::getToken() | 224 | 'token' => Session::getToken(), |
225 | 'theme' => $this->getTheme(), | ||
226 | 'poche_url' => Tools::getPocheUrl() | ||
123 | )); | 227 | )); |
124 | if (isset($_GET['install'])) { | 228 | if (isset($_GET['install'])) { |
125 | if (($_POST['password'] == $_POST['password_repeat']) | 229 | if (($_POST['password'] == $_POST['password_repeat']) |
@@ -139,13 +243,41 @@ class Poche | |||
139 | } | 243 | } |
140 | exit(); | 244 | exit(); |
141 | } | 245 | } |
246 | |||
247 | public function getTheme() { | ||
248 | return $this->currentTheme; | ||
249 | } | ||
250 | |||
251 | public function getInstalledThemes() { | ||
252 | $handle = opendir(THEME); | ||
253 | $themes = array(); | ||
254 | |||
255 | while (($theme = readdir($handle)) !== false) { | ||
256 | # Themes are stored in a directory, so all directory names are themes | ||
257 | # @todo move theme installation data to database | ||
258 | if (! is_dir(THEME . '/' . $theme) || in_array($theme, array('..', '.'))) { | ||
259 | continue; | ||
260 | } | ||
261 | |||
262 | $current = false; | ||
263 | |||
264 | if ($theme === $this->getTheme()) { | ||
265 | $current = true; | ||
266 | } | ||
267 | |||
268 | $themes[] = array('name' => $theme, 'current' => $current); | ||
269 | } | ||
270 | |||
271 | return $themes; | ||
272 | } | ||
142 | 273 | ||
143 | public function getDefaultConfig() | 274 | public function getDefaultConfig() |
144 | { | 275 | { |
145 | return array( | 276 | return array( |
146 | 'pager' => PAGINATION, | 277 | 'pager' => PAGINATION, |
147 | 'language' => LANG, | 278 | 'language' => LANG, |
148 | ); | 279 | 'theme' => DEFAULT_THEME |
280 | ); | ||
149 | } | 281 | } |
150 | 282 | ||
151 | /** | 283 | /** |
@@ -166,7 +298,7 @@ class Poche | |||
166 | } | 298 | } |
167 | $last_id = $this->store->getLastId($sequence); | 299 | $last_id = $this->store->getLastId($sequence); |
168 | if (DOWNLOAD_PICTURES) { | 300 | if (DOWNLOAD_PICTURES) { |
169 | $content = filtre_picture($parametres_url['body'], $url->getUrl(), $last_id); | 301 | $content = filtre_picture($content['body'], $url->getUrl(), $last_id); |
170 | Tools::logm('updating content article'); | 302 | Tools::logm('updating content article'); |
171 | $this->store->updateContent($last_id, $content, $this->user->getId()); | 303 | $this->store->updateContent($last_id, $content, $this->user->getId()); |
172 | } | 304 | } |
@@ -182,7 +314,7 @@ class Poche | |||
182 | } | 314 | } |
183 | 315 | ||
184 | if (!$import) { | 316 | if (!$import) { |
185 | Tools::redirect(); | 317 | Tools::redirect('?view=home'); |
186 | } | 318 | } |
187 | break; | 319 | break; |
188 | case 'delete': | 320 | case 'delete': |
@@ -230,7 +362,9 @@ class Poche | |||
230 | $prod = $this->getPocheVersion('prod'); | 362 | $prod = $this->getPocheVersion('prod'); |
231 | $compare_dev = version_compare(POCHE_VERSION, $dev); | 363 | $compare_dev = version_compare(POCHE_VERSION, $dev); |
232 | $compare_prod = version_compare(POCHE_VERSION, $prod); | 364 | $compare_prod = version_compare(POCHE_VERSION, $prod); |
365 | $themes = $this->getInstalledThemes(); | ||
233 | $tpl_vars = array( | 366 | $tpl_vars = array( |
367 | 'themes' => $themes, | ||
234 | 'dev' => $dev, | 368 | 'dev' => $dev, |
235 | 'prod' => $prod, | 369 | 'prod' => $prod, |
236 | 'compare_dev' => $compare_dev, | 370 | 'compare_dev' => $compare_dev, |
@@ -247,25 +381,37 @@ class Poche | |||
247 | $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); | 381 | $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); |
248 | $tidy->cleanRepair(); | 382 | $tidy->cleanRepair(); |
249 | $content = $tidy->value; | 383 | $content = $tidy->value; |
250 | } | 384 | |
251 | $tpl_vars = array( | 385 | # flattr checking |
386 | $flattr = new FlattrItem(); | ||
387 | $flattr->checkItem($entry['url']); | ||
388 | |||
389 | $tpl_vars = array( | ||
252 | 'entry' => $entry, | 390 | 'entry' => $entry, |
253 | 'content' => $content, | 391 | 'content' => $content, |
254 | ); | 392 | 'flattr' => $flattr |
393 | ); | ||
394 | } | ||
255 | } | 395 | } |
256 | else { | 396 | else { |
257 | Tools::logm('error in view call : entry is null'); | 397 | Tools::logm('error in view call : entry is null'); |
258 | } | 398 | } |
259 | break; | 399 | break; |
260 | default: # home view | 400 | default: # home, favorites and archive views |
261 | $entries = $this->store->getEntriesByView($view, $this->user->getId()); | 401 | $entries = $this->store->getEntriesByView($view, $this->user->getId()); |
262 | $this->pagination->set_total(count($entries)); | ||
263 | $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&'); | ||
264 | $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit()); | ||
265 | $tpl_vars = array( | 402 | $tpl_vars = array( |
266 | 'entries' => $datas, | 403 | 'entries' => '', |
267 | 'page_links' => $page_links, | 404 | 'page_links' => '', |
405 | 'nb_results' => '', | ||
268 | ); | 406 | ); |
407 | if (count($entries) > 0) { | ||
408 | $this->pagination->set_total(count($entries)); | ||
409 | $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&'); | ||
410 | $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit()); | ||
411 | $tpl_vars['entries'] = $datas; | ||
412 | $tpl_vars['page_links'] = $page_links; | ||
413 | $tpl_vars['nb_results'] = count($entries); | ||
414 | } | ||
269 | Tools::logm('display ' . $view . ' view'); | 415 | Tools::logm('display ' . $view . ' view'); |
270 | break; | 416 | break; |
271 | } | 417 | } |
@@ -303,6 +449,44 @@ class Poche | |||
303 | } | 449 | } |
304 | } | 450 | } |
305 | } | 451 | } |
452 | |||
453 | public function updateTheme() | ||
454 | { | ||
455 | # no data | ||
456 | if (empty($_POST['theme'])) { | ||
457 | } | ||
458 | |||
459 | # we are not going to change it to the current theme... | ||
460 | if ($_POST['theme'] == $this->getTheme()) { | ||
461 | $this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!')); | ||
462 | Tools::redirect('?view=config'); | ||
463 | } | ||
464 | |||
465 | $themes = $this->getInstalledThemes(); | ||
466 | $actualTheme = false; | ||
467 | |||
468 | foreach ($themes as $theme) { | ||
469 | if ($theme['name'] == $_POST['theme']) { | ||
470 | $actualTheme = true; | ||
471 | break; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | if (! $actualTheme) { | ||
476 | $this->messages->add('e', _('that theme does not seem to be installed')); | ||
477 | Tools::redirect('?view=config'); | ||
478 | } | ||
479 | |||
480 | $this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']); | ||
481 | $this->messages->add('s', _('you have changed your theme preferences')); | ||
482 | |||
483 | $currentConfig = $_SESSION['poche_user']->config; | ||
484 | $currentConfig['theme'] = $_POST['theme']; | ||
485 | |||
486 | $_SESSION['poche_user']->setConfig($currentConfig); | ||
487 | |||
488 | Tools::redirect('?view=config'); | ||
489 | } | ||
306 | 490 | ||
307 | /** | 491 | /** |
308 | * checks if login & password are correct and save the user in session. | 492 | * checks if login & password are correct and save the user in session. |
@@ -318,16 +502,7 @@ class Poche | |||
318 | if ($user != array()) { | 502 | if ($user != array()) { |
319 | # Save login into Session | 503 | # Save login into Session |
320 | Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user))); | 504 | Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user))); |
321 | |||
322 | $this->messages->add('s', _('welcome to your poche')); | 505 | $this->messages->add('s', _('welcome to your poche')); |
323 | if (!empty($_POST['longlastingsession'])) { | ||
324 | $_SESSION['longlastingsession'] = 31536000; | ||
325 | $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; | ||
326 | session_set_cookie_params($_SESSION['longlastingsession']); | ||
327 | } else { | ||
328 | session_set_cookie_params(0); | ||
329 | } | ||
330 | session_regenerate_id(true); | ||
331 | Tools::logm('login successful'); | 506 | Tools::logm('login successful'); |
332 | Tools::redirect($referer); | 507 | Tools::redirect($referer); |
333 | } | 508 | } |
diff --git a/inc/poche/PocheReadability.php b/inc/poche/PocheReadability.php new file mode 100644 index 00000000..48ae90d0 --- /dev/null +++ b/inc/poche/PocheReadability.php | |||
@@ -0,0 +1,46 @@ | |||
1 | <?php | ||
2 | |||
3 | class PocheReadability extends Readability | ||
4 | { | ||
5 | /** | ||
6 | * Get the article title as an H1. | ||
7 | * | ||
8 | * @return DOMElement | ||
9 | */ | ||
10 | protected function getArticleTitle() { | ||
11 | $curTitle = ''; | ||
12 | $origTitle = ''; | ||
13 | |||
14 | try { | ||
15 | $curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0)); | ||
16 | } catch(Exception $e) {} | ||
17 | |||
18 | if (preg_match('/ [\|\-] /', $curTitle)) | ||
19 | { | ||
20 | $curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle); | ||
21 | |||
22 | if (count(explode(' ', $curTitle)) < 3) { | ||
23 | $curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle); | ||
24 | } | ||
25 | } | ||
26 | else if(strlen($curTitle) > 150 || strlen($curTitle) < 15) | ||
27 | { | ||
28 | $hOnes = $this->dom->getElementsByTagName('h1'); | ||
29 | if($hOnes->length == 1) | ||
30 | { | ||
31 | $curTitle = $this->getInnerText($hOnes->item(0)); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | $curTitle = trim($curTitle); | ||
36 | |||
37 | if (count(explode(' ', $curTitle)) <= 4) { | ||
38 | $curTitle = $origTitle; | ||
39 | } | ||
40 | |||
41 | $articleTitle = $this->dom->createElement('h1'); | ||
42 | $articleTitle->innerHTML = $curTitle; | ||
43 | |||
44 | return $articleTitle; | ||
45 | } | ||
46 | } \ No newline at end of file | ||
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php index 3a792d43..8eb988f4 100644 --- a/inc/poche/Tools.class.php +++ b/inc/poche/Tools.class.php | |||
@@ -84,9 +84,9 @@ class Tools | |||
84 | 84 | ||
85 | public static function getTplFile($view) | 85 | public static function getTplFile($view) |
86 | { | 86 | { |
87 | $tpl_file = 'home.twig'; | 87 | $default_tpl = 'home.twig'; |
88 | switch ($view) | 88 | |
89 | { | 89 | switch ($view) { |
90 | case 'install': | 90 | case 'install': |
91 | $tpl_file = 'install.twig'; | 91 | $tpl_file = 'install.twig'; |
92 | break; | 92 | break; |
@@ -102,9 +102,20 @@ class Tools | |||
102 | case 'view': | 102 | case 'view': |
103 | $tpl_file = 'view.twig'; | 103 | $tpl_file = 'view.twig'; |
104 | break; | 104 | break; |
105 | |||
106 | case 'login': | ||
107 | $tpl_file = 'login.twig'; | ||
108 | break; | ||
109 | |||
110 | case 'error': | ||
111 | $tpl_file = 'error.twig'; | ||
112 | break; | ||
113 | |||
105 | default: | 114 | default: |
106 | break; | 115 | $tpl_file = $default_tpl; |
116 | break; | ||
107 | } | 117 | } |
118 | |||
108 | return $tpl_file; | 119 | return $tpl_file; |
109 | } | 120 | } |
110 | 121 | ||
@@ -228,24 +239,8 @@ class Tools | |||
228 | return $minutes; | 239 | return $minutes; |
229 | } | 240 | } |
230 | 241 | ||
231 | 242 | public static function getDocLanguage($userlanguage) { | |
232 | public static function createMyConfig() | 243 | $lang = explode('.', $userlanguage); |
233 | { | 244 | return str_replace('_', '-', $lang[0]); |
234 | $myconfig_file = './inc/poche/myconfig.inc.php'; | ||
235 | |||
236 | if (!is_writable('./inc/poche/')) { | ||
237 | self::logm('you don\'t have write access to create ./inc/poche/myconfig.inc.php'); | ||
238 | die('You don\'t have write access to create ./inc/poche/myconfig.inc.php.'); | ||
239 | } | ||
240 | |||
241 | if (!file_exists($myconfig_file)) | ||
242 | { | ||
243 | $fp = fopen($myconfig_file, 'w'); | ||
244 | fwrite($fp, '<?php'."\r\n"); | ||
245 | fwrite($fp, "define ('POCHE_VERSION', '1.0-beta4');" . "\r\n"); | ||
246 | fwrite($fp, "define ('SALT', '" . md5(time() . $_SERVER['SCRIPT_FILENAME'] . rand()) . "');" . "\r\n"); | ||
247 | fwrite($fp, "define ('LANG', 'en_EN.utf8');" . "\r\n"); | ||
248 | fclose($fp); | ||
249 | } | ||
250 | } | 245 | } |
251 | } \ No newline at end of file | 246 | } \ No newline at end of file |
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php index 5a893014..600a2166 100644 --- a/inc/poche/Url.class.php +++ b/inc/poche/Url.class.php | |||
@@ -354,7 +354,7 @@ class Url | |||
354 | } | 354 | } |
355 | if (isset($splink)) { | 355 | if (isset($splink)) { |
356 | // Build DOM tree from HTML | 356 | // Build DOM tree from HTML |
357 | $readability = new Readability($html, $url); | 357 | $readability = new PocheReadability($html, $url); |
358 | $xpath = new DOMXPath($readability->dom); | 358 | $xpath = new DOMXPath($readability->dom); |
359 | // Loop through single_page_link xpath expressions | 359 | // Loop through single_page_link xpath expressions |
360 | $single_page_url = null; | 360 | $single_page_url = null; |
diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php deleted file mode 100755 index a1917295..00000000 --- a/inc/poche/config.inc.php +++ /dev/null | |||
@@ -1,59 +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 | require_once __DIR__ . '/../../inc/poche/define.inc.php'; | ||
12 | |||
13 | # /!\ Be careful if you change the lines below /!\ | ||
14 | if (!file_exists(__DIR__ . '/../../vendor/autoload.php')) { | ||
15 | die('Twig does not seem installed. Have a look at <a href="http://inthepoche.com/?pages/Documentation">the documentation.</a>'); | ||
16 | } | ||
17 | |||
18 | // if (file_exists(__DIR__ . '/../../inc/poche/myconfig.inc.php')) { | ||
19 | // require_once __DIR__ . '/../../inc/poche/myconfig.inc.php'; | ||
20 | // } | ||
21 | require_once __DIR__ . '/../../inc/poche/User.class.php'; | ||
22 | require_once __DIR__ . '/../../inc/poche/Url.class.php'; | ||
23 | require_once __DIR__ . '/../../inc/3rdparty/class.messages.php'; | ||
24 | require_once __DIR__ . '/../../inc/poche/Poche.class.php'; | ||
25 | require_once __DIR__ . '/../../inc/3rdparty/Readability.php'; | ||
26 | require_once __DIR__ . '/../../inc/3rdparty/Encoding.php'; | ||
27 | require_once __DIR__ . '/../../inc/poche/Database.class.php'; | ||
28 | require_once __DIR__ . '/../../vendor/autoload.php'; | ||
29 | require_once __DIR__ . '/../../inc/3rdparty/simple_html_dom.php'; | ||
30 | require_once __DIR__ . '/../../inc/3rdparty/paginator.php'; | ||
31 | require_once __DIR__ . '/../../inc/3rdparty/Session.class.php'; | ||
32 | |||
33 | require_once __DIR__ . '/../../inc/3rdparty/simplepie/SimplePieAutoloader.php'; | ||
34 | require_once __DIR__ . '/../../inc/3rdparty/simplepie/SimplePie/Core.php'; | ||
35 | require_once __DIR__ . '/../../inc/3rdparty/content-extractor/ContentExtractor.php'; | ||
36 | require_once __DIR__ . '/../../inc/3rdparty/content-extractor/SiteConfig.php'; | ||
37 | require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/HumbleHttpAgent.php'; | ||
38 | require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php'; | ||
39 | require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/CookieJar.php'; | ||
40 | require_once __DIR__ . '/../../inc/3rdparty/feedwriter/FeedItem.php'; | ||
41 | require_once __DIR__ . '/../../inc/3rdparty/feedwriter/FeedWriter.php'; | ||
42 | require_once __DIR__ . '/../../inc/3rdparty/feedwriter/DummySingleItemFeed.php'; | ||
43 | |||
44 | if (DOWNLOAD_PICTURES) { | ||
45 | require_once __DIR__ . '/../../inc/poche/pochePictures.php'; | ||
46 | } | ||
47 | |||
48 | if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) { | ||
49 | date_default_timezone_set('UTC'); | ||
50 | } | ||
51 | |||
52 | $poche = new Poche(); | ||
53 | #XSRF protection with token | ||
54 | // if (!empty($_POST)) { | ||
55 | // if (!Session::isToken($_POST['token'])) { | ||
56 | // die(_('Wrong token')); | ||
57 | // } | ||
58 | // unset($_SESSION['tokens']); | ||
59 | // } \ No newline at end of file | ||
diff --git a/inc/poche/config.inc.php.new b/inc/poche/config.inc.php.new new file mode 100755 index 00000000..48cc5783 --- /dev/null +++ b/inc/poche/config.inc.php.new | |||
@@ -0,0 +1,56 @@ | |||
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 | define ('SALT', ''); # put a strong string here | ||
12 | define ('LANG', 'en_EN.utf8'); | ||
13 | |||
14 | define ('STORAGE', 'sqlite'); # postgres, mysql or sqlite | ||
15 | |||
16 | define ('STORAGE_SQLITE', ROOT . '/db/poche.sqlite'); # if you are using sqlite, where the database file is located | ||
17 | |||
18 | # only for postgres & mysql | ||
19 | define ('STORAGE_SERVER', 'localhost'); | ||
20 | define ('STORAGE_DB', 'poche'); | ||
21 | define ('STORAGE_USER', 'poche'); | ||
22 | define ('STORAGE_PASSWORD', 'poche'); | ||
23 | |||
24 | ################################################################################# | ||
25 | # Do not trespass unless you know what you are doing | ||
26 | ################################################################################# | ||
27 | |||
28 | define ('MODE_DEMO', FALSE); | ||
29 | define ('DEBUG_POCHE', true); | ||
30 | define ('DOWNLOAD_PICTURES', FALSE); | ||
31 | define ('CONVERT_LINKS_FOOTNOTES', FALSE); | ||
32 | define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); | ||
33 | define ('SHARE_TWITTER', TRUE); | ||
34 | define ('SHARE_MAIL', TRUE); | ||
35 | define ('SHARE_SHAARLI', FALSE); | ||
36 | define ('SHAARLI_URL', 'http://myshaarliurl.com'); | ||
37 | define ('FLATTR', TRUE); | ||
38 | define ('FLATTR_API', 'https://api.flattr.com/rest/v2/things/lookup/?url='); | ||
39 | define ('NOT_FLATTRABLE', '0'); | ||
40 | define ('FLATTRABLE', '1'); | ||
41 | define ('FLATTRED', '2'); | ||
42 | define ('ABS_PATH', 'assets/'); | ||
43 | |||
44 | define ('DEFAULT_THEME', 'default'); | ||
45 | |||
46 | define ('THEME', ROOT . '/themes'); | ||
47 | define ('LOCALE', ROOT . '/locale'); | ||
48 | define ('CACHE', ROOT . '/cache'); | ||
49 | |||
50 | define ('PAGINATION', '10'); | ||
51 | |||
52 | define ('POCHE_VERSION', '1.0-beta5'); | ||
53 | |||
54 | define ('IMPORT_POCKET_FILE', ROOT . '/ril_export.html'); | ||
55 | define ('IMPORT_READABILITY_FILE', ROOT . '/readability'); | ||
56 | define ('IMPORT_INSTAPAPER_FILE', ROOT . '/instapaper-export.html'); \ No newline at end of file | ||
diff --git a/inc/poche/define.inc.php b/inc/poche/define.inc.php index 3f667430..40f77b5c 100644 --- a/inc/poche/define.inc.php +++ b/inc/poche/define.inc.php | |||
@@ -3,7 +3,7 @@ | |||
3 | * poche, a read it later open source system | 3 | * poche, a read it later open source system |
4 | * | 4 | * |
5 | * @category poche | 5 | * @category poche |
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | 6 | * @author Nicolas Lœuillet <support@inthepoche.com> |
7 | * @copyright 2013 | 7 | * @copyright 2013 |
8 | * @license http://www.wtfpl.net/ see COPYING file | 8 | * @license http://www.wtfpl.net/ see COPYING file |
9 | */ | 9 | */ |
@@ -22,6 +22,11 @@ define ('SHARE_TWITTER', TRUE); | |||
22 | define ('SHARE_MAIL', TRUE); | 22 | define ('SHARE_MAIL', TRUE); |
23 | define ('SHARE_SHAARLI', FALSE); | 23 | define ('SHARE_SHAARLI', FALSE); |
24 | define ('SHAARLI_URL', 'http://myshaarliurl.com'); | 24 | define ('SHAARLI_URL', 'http://myshaarliurl.com'); |
25 | define ('FLATTR', TRUE); | ||
26 | define ('FLATTR_API', 'https://api.flattr.com/rest/v2/things/lookup/?url='); | ||
27 | define ('NOT_FLATTRABLE', '0'); | ||
28 | define ('FLATTRABLE', '1'); | ||
29 | define ('FLATTRED', '2'); | ||
25 | define ('ABS_PATH', 'assets/'); | 30 | define ('ABS_PATH', 'assets/'); |
26 | define ('TPL', __DIR__ . '/../../tpl'); | 31 | define ('TPL', __DIR__ . '/../../tpl'); |
27 | define ('LOCALE', __DIR__ . '/../../locale'); | 32 | define ('LOCALE', __DIR__ . '/../../locale'); |
diff --git a/inc/poche/global.inc.php b/inc/poche/global.inc.php new file mode 100644 index 00000000..65a026a7 --- /dev/null +++ b/inc/poche/global.inc.php | |||
@@ -0,0 +1,64 @@ | |||
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 | # the poche system root directory (/inc) | ||
12 | define('INCLUDES', dirname(__FILE__) . '/..'); | ||
13 | |||
14 | # the poche root directory | ||
15 | define('ROOT', INCLUDES . '/..'); | ||
16 | |||
17 | require_once INCLUDES . '/poche/Tools.class.php'; | ||
18 | require_once INCLUDES . '/poche/User.class.php'; | ||
19 | require_once INCLUDES . '/poche/Url.class.php'; | ||
20 | require_once INCLUDES . '/3rdparty/class.messages.php'; | ||
21 | require_once INCLUDES . '/poche/Poche.class.php'; | ||
22 | |||
23 | require_once INCLUDES . '/3rdparty/Readability.php'; | ||
24 | require_once INCLUDES . '/poche/PocheReadability.php'; | ||
25 | |||
26 | require_once INCLUDES . '/3rdparty/Encoding.php'; | ||
27 | require_once INCLUDES . '/poche/Database.class.php'; | ||
28 | require_once INCLUDES . '/3rdparty/simple_html_dom.php'; | ||
29 | require_once INCLUDES . '/3rdparty/paginator.php'; | ||
30 | require_once INCLUDES . '/3rdparty/Session.class.php'; | ||
31 | |||
32 | require_once INCLUDES . '/3rdparty/simplepie/SimplePieAutoloader.php'; | ||
33 | require_once INCLUDES . '/3rdparty/simplepie/SimplePie/Core.php'; | ||
34 | require_once INCLUDES . '/3rdparty/content-extractor/ContentExtractor.php'; | ||
35 | require_once INCLUDES . '/3rdparty/content-extractor/SiteConfig.php'; | ||
36 | require_once INCLUDES . '/3rdparty/humble-http-agent/HumbleHttpAgent.php'; | ||
37 | require_once INCLUDES . '/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php'; | ||
38 | require_once INCLUDES . '/3rdparty/humble-http-agent/CookieJar.php'; | ||
39 | require_once INCLUDES . '/3rdparty/feedwriter/FeedItem.php'; | ||
40 | require_once INCLUDES . '/3rdparty/feedwriter/FeedWriter.php'; | ||
41 | require_once INCLUDES . '/3rdparty/feedwriter/DummySingleItemFeed.php'; | ||
42 | require_once INCLUDES . '/3rdparty/FlattrItem.class.php'; | ||
43 | |||
44 | # Composer its autoloader for automatically loading Twig | ||
45 | if (! file_exists(ROOT . '/vendor/autoload.php')) { | ||
46 | Poche::$canRenderTemplates = false; | ||
47 | } else { | ||
48 | require_once ROOT . '/vendor/autoload.php'; | ||
49 | } | ||
50 | |||
51 | # system configuration; database credentials et cetera | ||
52 | if (! file_exists(INCLUDES . '/poche/config.inc.php')) { | ||
53 | Poche::$configFileAvailable = false; | ||
54 | } else { | ||
55 | require_once INCLUDES . '/poche/config.inc.php'; | ||
56 | } | ||
57 | |||
58 | if (Poche::$configFileAvailable && DOWNLOAD_PICTURES) { | ||
59 | require_once INCLUDES . '/poche/pochePictures.php'; | ||
60 | } | ||
61 | |||
62 | if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) { | ||
63 | date_default_timezone_set('UTC'); | ||
64 | } \ No newline at end of file | ||