PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/user/main.php

https://bitbucket.org/kxz/shimmie2
PHP | 482 lines | 436 code | 29 blank | 17 comment | 60 complexity | 75b2a613060bcaa3e837be664ceaba9f MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /*
  3. * Name: User Management
  4. * Author: Shish
  5. * Description: Allows people to sign up to the website
  6. */
  7. class UserBlockBuildingEvent extends Event {
  8. var $parts = array();
  9. public function add_link($name, $link, $position=50) {
  10. while(isset($this->parts[$position])) $position++;
  11. $this->parts[$position] = array("name" => $name, "link" => $link);
  12. }
  13. }
  14. class UserPageBuildingEvent extends Event {
  15. var $display_user;
  16. var $stats = array();
  17. public function __construct(User $display_user) {
  18. $this->display_user = $display_user;
  19. }
  20. public function add_stats($html, $position=50) {
  21. while(isset($this->stats[$position])) $position++;
  22. $this->stats[$position] = $html;
  23. }
  24. }
  25. class UserCreationEvent extends Event {
  26. var $username;
  27. var $password;
  28. var $email;
  29. public function __construct($name, $pass, $email) {
  30. $this->username = $name;
  31. $this->password = $pass;
  32. $this->email = $email;
  33. }
  34. }
  35. class UserCreationException extends SCoreException {}
  36. class UserPage extends SimpleExtension {
  37. public function onInitExt(Event $event) {
  38. global $config;
  39. $config->set_default_bool("login_signup_enabled", true);
  40. $config->set_default_int("login_memory", 365);
  41. $config->set_default_string("avatar_host", "none");
  42. $config->set_default_int("avatar_gravatar_size", 80);
  43. $config->set_default_string("avatar_gravatar_default", "");
  44. $config->set_default_string("avatar_gravatar_rating", "g");
  45. $config->set_default_bool("login_tac_bbcode", true);
  46. }
  47. public function onPageRequest(Event $event) {
  48. global $config, $database, $page, $user, $dokuwiki_landing;
  49. // user info is shown on all pages
  50. if($user->is_anonymous()) {
  51. $this->theme->display_login_block($page);
  52. }
  53. else {
  54. $ubbe = new UserBlockBuildingEvent();
  55. send_event($ubbe);
  56. ksort($ubbe->parts);
  57. $this->theme->display_user_block($page, $user, $ubbe->parts);
  58. }
  59. if($event->page_matches("user_admin")) {
  60. if($event->get_arg(0) == "login") {
  61. $page->set_mode("redirect");
  62. $page->set_redirect(wl($dokuwiki_landing, array("do" => "login"), true, '&'));
  63. }
  64. else if($event->get_arg(0) == "logout") {
  65. $page->set_mode("redirect");
  66. $page->set_redirect(wl($dokuwiki_landing, array("do" => "logout", "sectok" => getSecurityToken()), true, '&'));
  67. }
  68. else if($event->get_arg(0) == "change_pass") {
  69. $this->change_password_wrapper($page);
  70. }
  71. else if($event->get_arg(0) == "change_email") {
  72. $this->change_email_wrapper($page);
  73. }
  74. else if($event->get_arg(0) == "recover") {
  75. $user = User::by_name($_POST['username']);
  76. if(is_null($user)) {
  77. $this->theme->display_error($page, "Error", "There's no user with that name");
  78. }
  79. if(is_null($user->email)) {
  80. //
  81. }
  82. }
  83. else if($event->get_arg(0) == "create") {
  84. if(!$config->get_bool("login_signup_enabled")) {
  85. $this->theme->display_signups_disabled($page);
  86. }
  87. else if(!isset($_POST['name'])) {
  88. $this->theme->display_signup_page($page);
  89. }
  90. else if($_POST['pass1'] != $_POST['pass2']) {
  91. $this->theme->display_error($page, "Password Mismatch", "Passwords don't match");
  92. }
  93. else {
  94. try {
  95. if(!captcha_check()) {
  96. throw new UserCreationException("Error in captcha");
  97. }
  98. $uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']);
  99. send_event($uce);
  100. $this->set_login_cookie($uce->username, $uce->password);
  101. $page->set_mode("redirect");
  102. $page->set_redirect(make_link("user"));
  103. }
  104. catch(UserCreationException $ex) {
  105. $this->theme->display_error($page, "User Creation Error", $ex->getMessage());
  106. }
  107. }
  108. }
  109. else if($event->get_arg(0) == "set_more") {
  110. $this->set_more_wrapper($page);
  111. }
  112. else if($event->get_arg(0) == "list") {
  113. // select users.id,name,joindate,admin,
  114. // (select count(*) from images where images.owner_id=users.id) as images,
  115. // (select count(*) from comments where comments.owner_id=users.id) as comments from users;
  116. // select users.id,name,joindate,admin,image_count,comment_count
  117. // from users
  118. // join (select owner_id,count(*) as image_count from images group by owner_id) as _images on _images.owner_id=users.id
  119. // join (select owner_id,count(*) as comment_count from comments group by owner_id) as _comments on _comments.owner_id=users.id;
  120. $sort = "id";
  121. $order = "ASC";
  122. if (preg_match("/^id|name|min|average|max|count$/i", $event->get_arg(1))) {
  123. $sort = strtolower($event->get_arg(1));
  124. }
  125. if (preg_match("/^asc|desc$/i", $event->get_arg(2))) {
  126. $order = strtoupper($event->get_arg(2));
  127. }
  128. $rows = $database->db->GetAssoc("
  129. SELECT users.id AS uid,
  130. users.name AS name,
  131. min, average, max, count
  132. FROM users
  133. LEFT JOIN (
  134. SELECT images.owner_id,
  135. MIN(images.numeric_score) AS min,
  136. AVG(images.numeric_score) AS average,
  137. MAX(images.numeric_score) AS max,
  138. COUNT(images.numeric_score) AS count
  139. FROM images
  140. GROUP BY images.owner_id
  141. ) AS images
  142. ON users.id = images.owner_id
  143. ORDER BY $sort $order
  144. ");
  145. $this->theme->display_user_list($page, $rows);
  146. }
  147. }
  148. if(($event instanceof PageRequestEvent) && $event->page_matches("user")) {
  149. $display_user = ($event->count_args() == 0) ? $user : User::by_name($event->get_arg(0));
  150. if($event->count_args() == 0 && $user->is_anonymous()) {
  151. $this->theme->display_error($page, "Not Logged In",
  152. "You aren't logged in. First do that, then you can see your stats.");
  153. }
  154. else if(!is_null($display_user)) {
  155. send_event(new UserPageBuildingEvent($display_user));
  156. }
  157. else {
  158. $this->theme->display_error($page, "No Such User",
  159. "If you typed the ID by hand, try again; if you came from a link on this ".
  160. "site, it might be bug report time...");
  161. }
  162. }
  163. }
  164. public function onUserPageBuilding(Event $event) {
  165. global $page, $user, $config;
  166. $h_join_date = html_escape($event->display_user->join_date);
  167. $event->add_stats("Join date: $h_join_date", 10);
  168. $av = $event->display_user->get_avatar_html();
  169. if($av) $event->add_stats($av, 0);
  170. ksort($event->stats);
  171. $this->theme->display_user_page($event->display_user, $event->stats);
  172. if($user->id == $event->display_user->id) {
  173. $ubbe = new UserBlockBuildingEvent();
  174. send_event($ubbe);
  175. ksort($ubbe->parts);
  176. $this->theme->display_user_links($page, $user, $ubbe->parts);
  177. }
  178. if(
  179. ($user->is_admin() || $user->id == $event->display_user->id) &&
  180. ($user->id != $config->get_int('anon_id'))
  181. ) {
  182. $this->theme->display_ip_list(
  183. $page,
  184. $this->count_upload_ips($event->display_user),
  185. $this->count_comment_ips($event->display_user));
  186. }
  187. }
  188. public function onSetupBuilding(Event $event) {
  189. global $config;
  190. $hosts = array(
  191. "None" => "none",
  192. "Gravatar" => "gravatar"
  193. );
  194. $sb = new SetupBlock("User Options");
  195. $sb->add_bool_option("login_signup_enabled", "Allow new signups: ");
  196. $sb->add_longtext_option("login_tac", "<br>Terms &amp; Conditions:<br>");
  197. $sb->add_choice_option("avatar_host", $hosts, "<br>Avatars: ");
  198. if($config->get_string("avatar_host") == "gravatar") {
  199. $sb->add_label("<br>&nbsp;<br><b>Gravatar Options</b>");
  200. $sb->add_choice_option("avatar_gravatar_type",
  201. array(
  202. 'Default'=>'default',
  203. 'Wavatar'=>'wavatar',
  204. 'Monster ID'=>'monsterid',
  205. 'Identicon'=>'identicon'
  206. ),
  207. "<br>Type: ");
  208. $sb->add_choice_option("avatar_gravatar_rating",
  209. array('G'=>'g', 'PG'=>'pg', 'R'=>'r', 'X'=>'x'),
  210. "<br>Rating: ");
  211. }
  212. $event->panel->add_block($sb);
  213. }
  214. public function onUserBlockBuilding(Event $event) {
  215. $event->add_link("My Profile", make_link("user"));
  216. $event->add_link("User List", make_link("user_admin/list"), -1);
  217. $event->add_link("Log Out", make_link("user_admin/logout"), 99);
  218. }
  219. public function onUserCreation(Event $event) {
  220. $this->check_user_creation($event);
  221. $this->create_user($event);
  222. }
  223. public function onSearchTermParse(Event $event) {
  224. $matches = array();
  225. if(preg_match("/^(poster|user)=(.*)$/i", $event->term, $matches)) {
  226. $user = User::by_name($matches[2]);
  227. if(!is_null($user)) {
  228. $user_id = $user->id;
  229. }
  230. else {
  231. $user_id = -1;
  232. }
  233. $event->add_querylet(new Querylet("images.owner_id = $user_id"));
  234. }
  235. else if(preg_match("/^(poster|user)_id=([0-9]+)$/i", $event->term, $matches)) {
  236. $user_id = int_escape($matches[2]);
  237. $event->add_querylet(new Querylet("images.owner_id = $user_id"));
  238. }
  239. }
  240. // }}}
  241. // Things done *with* the user {{{
  242. private function login($page) {
  243. global $user;
  244. $name = $_POST['user'];
  245. $pass = $_POST['pass'];
  246. $hash = md5(strtolower($name) . $pass);
  247. $duser = User::by_name_and_hash($name, $hash);
  248. if(!is_null($duser)) {
  249. $user = $duser;
  250. $this->set_login_cookie($name, $pass);
  251. if($user->is_admin()) {
  252. log_warning("user", "Admin logged in");
  253. }
  254. else {
  255. log_info("user", "User logged in");
  256. }
  257. $page->set_mode("redirect");
  258. $page->set_redirect(make_link("user"));
  259. }
  260. else {
  261. log_warning("user", "Failed to log in as ".html_escape($name)." [$hash]");
  262. $this->theme->display_error($page, "Error", "No user with those details was found");
  263. }
  264. }
  265. private function check_user_creation($event) {
  266. $name = $event->username;
  267. $pass = $event->password;
  268. $email = $event->email;
  269. global $database;
  270. if(strlen($name) < 1) {
  271. throw new UserCreationException("Username must be at least 1 character");
  272. }
  273. else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $name)) {
  274. throw new UserCreationException(
  275. "Username contains invalid characters. Allowed characters are ".
  276. "letters, numbers, dash, and underscore");
  277. }
  278. else if($database->db->GetRow("SELECT * FROM users WHERE name = ?", array($name))) {
  279. throw new UserCreationException("That username is already taken");
  280. }
  281. }
  282. private function create_user($event) {
  283. global $database;
  284. $hash = md5(strtolower($event->username) . $event->password);
  285. $email = (!empty($event->email)) ? $event->email : null;
  286. // if there are currently no admins, the new user should be one
  287. $need_admin = ($database->db->GetOne("SELECT COUNT(*) FROM users WHERE admin IN ('Y', 't', '1')") == 0);
  288. $admin = $need_admin ? 'Y' : 'N';
  289. $database->Execute(
  290. "INSERT INTO users (name, pass, joindate, email, admin) VALUES (?, ?, now(), ?, ?)",
  291. array($event->username, $hash, $email, $admin));
  292. $uid = $database->db->Insert_ID();
  293. log_info("user", "Created User #$uid ({$event->username})");
  294. }
  295. private function set_login_cookie($name, $pass) {
  296. global $config;
  297. $addr = get_session_ip($config);
  298. $hash = md5(strtolower($name) . $pass);
  299. set_prefixed_cookie("user", $name,
  300. time()+60*60*24*365, '/');
  301. set_prefixed_cookie("session", md5($hash.$addr),
  302. time()+60*60*24*$config->get_int('login_memory'), '/');
  303. }
  304. //}}}
  305. // Things done *to* the user {{{
  306. private function change_password_wrapper($page) {
  307. global $user;
  308. global $config;
  309. global $database;
  310. if($user->is_anonymous()) {
  311. $this->theme->display_error($page, "Error", "You aren't logged in");
  312. }
  313. else if(isset($_POST['id']) && isset($_POST['pass1']) && isset($_POST['pass2'])) {
  314. $id = $_POST['id'];
  315. $pass1 = $_POST['pass1'];
  316. $pass2 = $_POST['pass2'];
  317. $duser = User::by_id($id);
  318. if((!$user->is_admin()) && ($duser->name != $user->name)) {
  319. $this->theme->display_error($page, "Error",
  320. "You need to be an admin to change other people's passwords");
  321. }
  322. else if($pass1 != $pass2) {
  323. $this->theme->display_error($page, "Error", "Passwords don't match");
  324. }
  325. else {
  326. // FIXME: send_event()
  327. $duser->set_password($pass1);
  328. if($id == $user->id) {
  329. $this->set_login_cookie($duser->name, $pass1);
  330. $page->set_mode("redirect");
  331. $page->set_redirect(make_link("user"));
  332. }
  333. else {
  334. $page->set_mode("redirect");
  335. $page->set_redirect(make_link("user/{$duser->name}"));
  336. }
  337. }
  338. }
  339. }
  340. private function change_email_wrapper($page) {
  341. global $user;
  342. global $config;
  343. global $database;
  344. if($user->is_anonymous()) {
  345. $this->theme->display_error($page, "Error", "You aren't logged in");
  346. }
  347. else if(isset($_POST['id']) && isset($_POST['address'])) {
  348. $id = $_POST['id'];
  349. $address = $_POST['address'];
  350. $duser = User::by_id($id);
  351. if((!$user->is_admin()) && ($duser->name != $user->name)) {
  352. $this->theme->display_error($page, "Error",
  353. "You need to be an admin to change other people's addressess");
  354. }
  355. else {
  356. $duser->set_email($address);
  357. if($id == $user->id) {
  358. $page->set_mode("redirect");
  359. $page->set_redirect(make_link("user"));
  360. }
  361. else {
  362. $page->set_mode("redirect");
  363. $page->set_redirect(make_link("user/{$duser->name}"));
  364. }
  365. }
  366. }
  367. }
  368. private function set_more_wrapper($page) {
  369. global $user;
  370. global $config;
  371. global $database;
  372. $page->set_title("Error");
  373. $page->set_heading("Error");
  374. $page->add_block(new NavBlock());
  375. if(!$user->is_admin()) {
  376. $page->add_block(new Block("Not Admin", "Only admins can edit accounts"));
  377. }
  378. else if(!isset($_POST['id']) || !is_numeric($_POST['id'])) {
  379. $page->add_block(new Block("No ID Specified",
  380. "You need to specify the account number to edit"));
  381. }
  382. else {
  383. $admin = (isset($_POST['admin']) && ($_POST['admin'] == "on"));
  384. $duser = User::by_id($_POST['id']);
  385. $duser->set_admin($admin);
  386. $page->set_mode("redirect");
  387. if($duser->id == $user->id) {
  388. $page->set_redirect(make_link("user"));
  389. }
  390. else {
  391. $page->set_redirect(make_link("user/{$duser->name}"));
  392. }
  393. }
  394. }
  395. // }}}
  396. // ips {{{
  397. private function count_upload_ips($duser) {
  398. global $database;
  399. $rows = $database->db->GetAssoc("
  400. SELECT
  401. owner_ip,
  402. COUNT(images.id) AS count,
  403. MAX(posted) AS most_recent
  404. FROM images
  405. WHERE owner_id=?
  406. GROUP BY owner_ip
  407. ORDER BY most_recent DESC", array($duser->id), false, true);
  408. return $rows;
  409. }
  410. private function count_comment_ips($duser) {
  411. global $database;
  412. $rows = $database->db->GetAssoc("
  413. SELECT
  414. owner_ip,
  415. COUNT(comments.id) AS count,
  416. MAX(posted) AS most_recent
  417. FROM comments
  418. WHERE owner_id=?
  419. GROUP BY owner_ip
  420. ORDER BY most_recent DESC", array($duser->id), false, true);
  421. return $rows;
  422. }
  423. // }}}
  424. }
  425. add_event_listener(new UserPage());
  426. ?>