UsersController.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. namespace app\controllers;
  3. use lithium\storage\Session;
  4. use app\models\User;
  5. use app\models\confirmKey;
  6. use app\models\ProfilePic;
  7. use app\models\Post;
  8. use lithium\security\Auth;
  9. use lithium\util\String;
  10. use \MongoDate;
  11. use li3_flash_message\extensions\storage\FlashMessage;
  12. use app\libraries\openID\LightOpenID;
  13. class UsersController extends \lithium\action\Controller {
  14. public $secret = "marshmellows"; //I don't know why either?
  15. //Make login a public action.
  16. public $publicActions = array('login', 'logout', 'signup', 'confirm');
  17. /* User profile implementation */
  18. /* So, like twitter if you friend someone but they don't friend you,
  19. * You can see their posts, but they don't see yours. Furthermore, posts that start with a @username
  20. * shouldn't be shown to the user either.
  21. *
  22. * Now say for instance the the user isn't logged in but visits the profile of another user. It should show the users posts
  23. * that aren't set to private. this should be as easy as Posts::find('all', array('conditions' => array('level' != private)))
  24. * However, we need to differentiate posts that are Hidden (posts that begin with a @username), Friends only (set by user) or
  25. * Public (by default all posts.) there should be a method that filters the new post method, that takes a post, determines
  26. * what access level it should be then passes that back to the calling method (in save?)
  27. *
  28. * Of course, for logged in users, we need to do a multiselect I guess, something like find posts where access level is hidden
  29. * friends only, and public, ordered by date (descending) limit 20 or so (use pagination plugin)
  30. *
  31. * Finally, there should be an an option to make all posts private, this can be done by the postlevel() method, it can check i
  32. * the user has the private option set, then if true return private for all posts :)
  33. */
  34. public function index($username = null)
  35. {
  36. //If no username was entered,
  37. if ($username == null)
  38. {
  39. //Show a user list?
  40. // TODO: Pagination
  41. //Show a list of all users
  42. $users = User::all();
  43. return compact('users');
  44. //maybe we can use the endpoint hook and show a "network activity feed"
  45. //For example show all public posts as they get posted using ajax ?
  46. //Maybe we should have a route for this? /activity maybe?
  47. }
  48. //Otherwise get that user
  49. $user = User::find('first', array('conditions' => compact('username')));
  50. //find all posts, sort by date, order descending, limit to 20
  51. $feed;
  52. //Dont know if php will keep counts return on the stack so I'm storing it to avoid calling it 3 times.
  53. $tmpCount = count($user->feed);
  54. //If the user has 20 or more posts then query 20, otherwise query the number of posts the user has
  55. $count = ($tmpCount >= 20) ? 20 : $tmpCount;
  56. //If the user is logged in
  57. $user = Auth::check('default');
  58. if ($user)
  59. {
  60. if ($user->username == $username)
  61. {
  62. $feed = $user->getPosts(array('level' => null));
  63. return compact($feed, $user);
  64. }
  65. //if the user is logged in and the users are friends
  66. if (User::areFriend(Auth::check('default'), $user))
  67. {
  68. $feed = $user->getPosts(array('level' => null));
  69. return compact($feed, $user);
  70. }
  71. } //If they aren't friend, or if the user isn't logged in it defaults to the public version (filter private posts)
  72. //If the user isn't logged in, then we should show a signup partial like twitter
  73. else
  74. {
  75. //find all non-private posts, limit 20, order descending (decending was taken care of with array_unshift btw)
  76. $feed = $user->getPost(array('level' => array('$ne' => "private")));
  77. return compact($feed, $user);
  78. }
  79. }
  80. /**
  81. * How this method should work:
  82. * Since this network is default open like twitter, there is three types of relationships:
  83. * 1) Mutually Oblivious: Neither user knows each other.
  84. * 2) You Follow: You follow a user and sees their non-private posts
  85. * 3) Friends: being friends allows you to see each others privates posts
  86. * (@see app\models\post) for an example of how this works
  87. *
  88. * Now, for now there are two types of requests: Friend requests which causes both users to automatically follow eachother
  89. * And follows. Follows are unidirectional and thus don't require the followees permission
  90. */
  91. public function follow($username = null)
  92. {
  93. $status;
  94. $message;
  95. if($this->request->data)
  96. {
  97. $user = Auth::check('default');
  98. if ($user)
  99. {
  100. $user = User::find('first', array('conditions' => array('username' => $user['username'])));
  101. if ($user->follow($username))
  102. {
  103. $status = true;
  104. return array('sucseeded' => true, 'message' => 'You are now following $username');
  105. }
  106. }
  107. }
  108. }
  109. /**
  110. * Calls the user to addFriend method, then returns the status.
  111. * @param username The name of the user to add as a friend
  112. */
  113. public function addfriend($username = null)
  114. {
  115. }
  116. public function reconfirm()
  117. {
  118. //Search the database for email address
  119. if (ConfirmKey::count(array('conditions' => array('email' => $this->request->data['email']))) != 0)
  120. {
  121. //Resend the confirmation email
  122. }
  123. else
  124. {
  125. //Show them a message
  126. }
  127. //Send a new key
  128. }
  129. public function profile($username)
  130. {
  131. //If no username is passed in.
  132. if (empty($username))
  133. {
  134. $this->redirect('/');
  135. }
  136. //Otherwise
  137. else
  138. {
  139. //Find that user, and go to their profile page.
  140. $user = User::find('first', array('conditions' => compact('username')));
  141. //$photo = (empty($user->profilepic) ? ProfilePic::create() : $user->profilepic);
  142. return compact('user', 'photo');
  143. //Render the profile layout
  144. }
  145. }
  146. public function post()
  147. {
  148. if ($this->request->data)
  149. {
  150. $user = Auth::check('default');
  151. if ($user) {
  152. $user = User::find($user['_id']);
  153. $user->post($this->request->data);
  154. }
  155. /* :TODO: Need to return a status here */
  156. $this->redirect('Users::feed');
  157. }
  158. }
  159. public function feed()
  160. {
  161. //Get the currently logged in user
  162. $user = Auth::check('default');
  163. //If there is a user logged in (There should be since feed isn't a public function)
  164. if ($user)
  165. {
  166. //Get that user from the database
  167. $user = User::find('first', array('conditions' => array('username' => $user['username'])));
  168. //Set the feed variable scope (since we are going to use it outside the loop)
  169. $feed;
  170. if (isset($user->feed)) {
  171. //For each post ID in $users feed,
  172. foreach ($user->feed as $post)
  173. {
  174. //Find the post by it's ID
  175. $post = Post::find($post);
  176. //If a post was found,
  177. if (!empty($post))
  178. {
  179. //Add it to the feed
  180. $feed[] = $post;
  181. }
  182. //Else we should remove the the ID from the users feed.
  183. }
  184. /* new posts are appended to the end of the feed array, therefore new posts naturally end up at the bottom of the feed
  185. * therefore, we reverse the order of the array so that new posts end up at the top of the feed.
  186. * This is probably faster than doing sorting by date at the database level, though it for some reason
  187. * posts don't get inserted in the right order it could cause them to come out wrong in the view */
  188. $feed = array_reverse($feed);
  189. //This renders a custom layout we use for the feed, then passes user and feed to the view for the variables.
  190. }
  191. return compact('user', 'feed');
  192. }
  193. }
  194. public function openid()
  195. {
  196. if ($this->request->data)
  197. {
  198. if (!empty($this->request->query))
  199. {
  200. var_dump($this->request->query);
  201. }
  202. else
  203. {
  204. $openid = new LightOpenID;
  205. echo $openid->validates();
  206. }
  207. }
  208. }
  209. public function signup()
  210. {
  211. //If the request isn't empty
  212. if($this->request->data)
  213. {
  214. //Create a user from the data
  215. $user = User::Create($this->request->data);
  216. //Until the save bug is fixed
  217. $results = $user->validates();
  218. if ($results)
  219. {
  220. //The user isn't active until after they confirm.
  221. $user->confirmed = false;
  222. $user->active = false;
  223. $user->joinedOn = new MongoDate();
  224. //Generate a confirmation key for the user
  225. $key = confirmKey::Create(array('key' => confirmKey::generate($user->email), 'username' => $user->username));
  226. //Save it to the database
  227. $key->save();
  228. //If everything goes ok
  229. if ($user->save(null, array('validates' => false)))
  230. {
  231. //Store some session information
  232. //Session::write('username', $user->username);
  233. //Session::write('email', $user->email);
  234. //For the debug version, send the key to the front page
  235. $link = "/users/confirm";
  236. return compact('key', 'link');
  237. // /*
  238. // //Send them to the confirmation page.
  239. // $this->redirect('users/confirm');
  240. }
  241. }
  242. else
  243. {
  244. return compact('user');
  245. }
  246. }
  247. }
  248. public function login($location = null, $args = null)
  249. {
  250. //Put in a check to make sure the user has confirmed their account
  251. //The check should probably happen below after the auth check.
  252. /*
  253. If the user is valid, but not confirmed,
  254. tell the user they haven't confirmed,
  255. offer to resend the confirmation email or changed their email address.
  256. */
  257. if (!empty($this->request->data)) {
  258. $user = Auth::check('default', $this->request);
  259. if ($user)
  260. {
  261. $user = User::find('first', array('conditions' => array('username' => $user['username'])));
  262. $user->lastLogin = new MongoDate();
  263. $user->save(null, array('validate' => false));
  264. //If the user hasn't confirmed their account
  265. if(!$user->confirmed)
  266. {
  267. //Redirect them to the confirmation page.
  268. return $this->redirect('Users::confirm');
  269. }
  270. //If the user's account is not active they are probably banned
  271. if (!$user->active)
  272. {
  273. return $this->redirect('/pages/banned');
  274. }
  275. //If the user was trying to go somewhere, redirect them there
  276. $loc = Session::read('url');
  277. if (isset($loc))
  278. {
  279. Session::delete('url');
  280. return $this->redirect(array('controller' => $loc['controller'],
  281. 'action' => $loc['action'],
  282. 'args' => $loc['args']
  283. ));
  284. }
  285. //Otherwise send them to the hompa
  286. return $this->redirect('Users::feed');
  287. }
  288. else
  289. {
  290. FlashMessage::write('Username or Password Incorrect.');
  291. }
  292. }
  293. }
  294. //Logout
  295. public function logout()
  296. {
  297. //If the user logs out
  298. //Clear their auth cookie
  299. Auth::Clear('default');
  300. //Redirect them to the homepage.
  301. return $this->redirect('/');
  302. }
  303. public function changePassword()
  304. {
  305. //Get the user to verify their current password
  306. $input = $this->request->data;
  307. //If there is inputfrom the form
  308. if ($input)
  309. {
  310. //Get the user from auth
  311. $user = Auth::check('default');
  312. //Ensure that the passwords are the same.
  313. if ($input['newpass'] != $input['confirm'])
  314. {
  315. //return error
  316. return $this->redirect('/');
  317. }
  318. else if(!empty($user) && isset($input['newpass']))
  319. {
  320. //find the user by their ID
  321. $user = User::find($user['_id']);
  322. //Set the newpassword, this triggers the hash function in ->save()
  323. $user->newpass = $input['newpass'];
  324. //Save the data
  325. if ($user->save(null, array('validate' => false))) {
  326. //Tell the user their password was updated sucsessfully
  327. }
  328. //Else there was an error, so send them away
  329. /* If the compare is changed to a validator
  330. * returning the user object will show the error in the view.*/
  331. return compact('user');
  332. }
  333. }
  334. }
  335. public function settings()
  336. {
  337. //Get the user using their login information (from auth)
  338. $user = Auth::check('default');
  339. $user = User::find($user['_id']);
  340. //If the request isn't empty
  341. if(!empty($this->request->data)) {
  342. //Foreach key/value pair in the request data
  343. foreach($this->request->data as $key => $value)
  344. {
  345. //TODO: Make sure to lock the schema to prevent wierd values
  346. $user->$key = $value;
  347. }
  348. //Save the user.
  349. $user->save(null, array('validate' => false));
  350. }
  351. return compact('user');
  352. }
  353. public function requestFriend($username) {
  354. //If the user isn't blocking this user,
  355. //And the user doesn't have private set
  356. //Send them a DM with a confirm key
  357. $key = confirmKey::create();
  358. $thisUser = auth::check('default');
  359. $link = Html::link('here', "/users/confirmFriend/$this->username/$key->key");
  360. $post = Post::create(array('body' => "$thisUser->username want's to be your friend. Click $link to confirm"));
  361. $post->directMessage($username);
  362. }
  363. /* Potential hack here, in theory, a user could sign up a new account
  364. * then send a direct message manually using the confirm key from the email.
  365. */
  366. public function confirmFriend($username, $key) {
  367. /* Normally we could try and find the cKey, then if it doesn't exist
  368. * Do something about it,
  369. * However, ConfirmKey::validates basically counts the number of keys that
  370. * match $key, this is probably better than find since ->validates() may be a bit
  371. * more efficent */
  372. $cKey = confirmKey::create(compact('key'));
  373. if ($cKey->validates())
  374. {
  375. $thisUser = Auth::check('default');
  376. $thisUser = User::find('first', array('conditions' => array('username' => $thisUser['username'])));
  377. $requestingUser = User::find('first', array('conditions' => compact('username')));
  378. $requestingUser->addFriend($thisUser->username);
  379. $thisUser->addFriend($requetingUser->username);
  380. //Some action here if true :TODO:
  381. }
  382. //Some action here if false
  383. }
  384. public function confirm($key = null)
  385. {
  386. //Situation one
  387. //They have a key
  388. if (!(empty($key)))
  389. {
  390. //Find the key in the database
  391. $foundKey = confirmKey::find('first', array('conditions' => compact('key')));
  392. //If the key exists
  393. if($foundKey != NULL)
  394. {
  395. /* Note: foundKey->validates() does the same check, but it was added incase more validation is needed */
  396. //Find that user in the database
  397. $foundUser = User::find('first', array('conditions' => array("username" => $foundKey->username)));
  398. $valid = ($foundUser != NULL);
  399. //Set the users account active and confirmed.
  400. $foundUser->confirmed = true;
  401. $foundUser->active = true;
  402. //If the user is saved sucsessfully,
  403. if($foundUser->save(null, array('validate' => false)))
  404. {
  405. /* If the save is sucsessful we are done */
  406. //Delete their key,
  407. $foundKey->delete();
  408. //Send them to the homepage (probably login though)
  409. $this->redirect("/");
  410. }
  411. else
  412. {
  413. FlashMessage::set("There was an error.");
  414. }
  415. }
  416. else
  417. {
  418. //Otherwise
  419. FlashMessage::set("There was an error finding the key.");
  420. return;
  421. }
  422. }
  423. }
  424. public function step2()
  425. {
  426. //Check that step1 is completed sucsessfully,
  427. //Then take them to their profile in edit mode
  428. }
  429. }
  430. ?>