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

/app/models/user.php

https://code.google.com/
PHP | 441 lines | 310 code | 74 blank | 57 comment | 26 complexity | 78a27747a6eea06e2644af27781a02da MD5 | raw file
  1. <?php
  2. include_model('ban, tag, user_blacklisted_tag');
  3. has_one('ban', array('foreign_key' => 'user_id'));
  4. has_one('user_blacklisted_tag');
  5. belongs_to('avatar_post', array('model_name' => "Post", 'foreign_key' => 'avatar_post_id'));
  6. before('validation', 'commit_secondary_languages');
  7. before('create', 'can_signup, set_role');
  8. before('save', 'encrypt_password');
  9. after('create', 'set_default_blacklisted_tags, increment_count');
  10. after('save', 'commit_blacklists');
  11. after('destroy', 'decrement_count');
  12. validates(array(
  13. 'name' => array(
  14. 'length' => '2..20',
  15. 'format' => array('/\A[^\s;,]+\Z/', 'on' => 'create', 'message' => 'cannot have whitespace, commas, or semicolons'),
  16. 'uniqueness' => array(true,'on' => 'create')
  17. ),
  18. 'password' => array(
  19. 'length' => array('>=5', 'if' => array('property_exists' => 'password')),
  20. 'confirmation' => true,
  21. )
  22. ));
  23. // # validates_format_of :name, :with => /^(Anonymous|[Aa]dministrator)/, :on => :create, :message => "this is a disallowed username"
  24. // m.after_save :update_cached_name if CONFIG["enable_caching"]
  25. // m.has_many :tag_subscriptions, :dependent => :delete_all, :order => "name"
  26. // m.validates_format_of :language, :with => /^([a-z\-]+)|$/
  27. // m.validates_format_of :secondary_languages, :with => /^([a-z\-]+(,[a-z\0]+)*)?$/
  28. class User extends ActiveRecord {
  29. static $current;
  30. function _construct() {
  31. if(isset($this->is_anonymous))
  32. return;
  33. elseif(isset($this->id))
  34. $this->is_anonymous = false;
  35. // if(CONFIG::show_samples)
  36. // $this->show_samples = false;
  37. }
  38. static function authenticate($name, $pass) {
  39. return self::authenticate_hash($name, md5($name.$pass));
  40. }
  41. function user_info_cookie() {
  42. return $this->id . ';' . $this->level . ';' . ($this->use_browser ? "1":"0");
  43. }
  44. static function authenticate_hash($name, $pass) {
  45. $user = parent::find('first', array('conditions' => array("lower(name) = lower(?) AND password_hash = ?", $name, $pass)));
  46. return $user;
  47. }
  48. function has_permission($record, $foreign_key = 'user_id') {
  49. if($this->level >= 40 || $record->$foreign_key == $this->id)
  50. return true;
  51. else
  52. return false;
  53. }
  54. function pretty_name($name = null) {
  55. // $name = !isset($this->name) ? $name : $this->name;
  56. return str_replace('_', ' ', $this->name);
  57. }
  58. function pretty_level() {
  59. return array_search($this->level, CONFIG::$user_levels);
  60. }
  61. static function save_cookies($user) {
  62. cookie_put('login', $user->name);
  63. cookie_put('pass_hash', $user->password_hash);
  64. $_SESSION[CONFIG::app_name]['user_id'] = $user->id;
  65. }
  66. function secondary_language_array() {
  67. if (!isset($this->secondary_language_array))
  68. $this->secondary_language_array = explode(',', $this->secondary_languages);
  69. return $this->secondary_language_array;
  70. }
  71. function blacklisted_tags() {
  72. if ($this->user_blacklisted_tag)
  73. return $this->user_blacklisted_tag->tags;
  74. else
  75. return null;
  76. }
  77. function blacklisted_tags_array() {
  78. if ($this->user_blacklisted_tag)
  79. return explode("\r\n", $this->user_blacklisted_tag->tags);
  80. else
  81. return array();
  82. // user_blacklisted_tags.map {|x| x.tags}
  83. }
  84. function commit_blacklists() {
  85. if (!empty($this->user_blacklisted_tag) && isset($this->blacklisted_tags))
  86. $this->user_blacklisted_tag->update_attribute('tags', $this->blacklisted_tags);
  87. // if ($this->blacklisted_tags) {
  88. // $this->user_blacklisted_tags = null;
  89. // @blacklisted_tags.scan(/[^\r\n]+/).each do |tags|
  90. // user_blacklisted_tags.create(:tags => tags)
  91. // end
  92. // }
  93. }
  94. function can_change($record, $attribute) {
  95. if (self::is('>=40'))
  96. return true;
  97. $method = "can_change_${attribute}";
  98. if (method_exists($record, $method))
  99. return $record->$method($this);
  100. elseif (method_exists($record, 'can_change'))
  101. return $record->can_change($this, $attribute);
  102. else
  103. return false;
  104. }
  105. static function is($comparison) {
  106. $current_level = (int)self::$current->level;
  107. $r = num_compare($current_level, $comparison);
  108. return $r;
  109. }
  110. function set_default_blacklisted_tags() {
  111. // foreach (CONFIG::$default_blacklists as $b)
  112. UserBlacklistedTag::create(array('user_id' => $this->id, 'tags' => implode("\r\n", CONFIG::$default_blacklists)));
  113. // CONFIG["default_blacklists"].each do |b|
  114. // UserBlacklistedTag.create(:user_id => self.id, :tags => b)
  115. // end
  116. }
  117. function uploaded_tags($options = array()) {
  118. $type = !empty($options['type']) ? $options['type'] : null;
  119. // if CONFIG["enable_caching"]
  120. // uploaded_tags = Cache.get("uploaded_tags/#{id}/#{type}")
  121. // return uploaded_tags unless uploaded_tags == nil
  122. // end
  123. // if RAILS_ENV == "test"
  124. // # disable filtering in test mode to simplify tests
  125. // popular_tags = ""
  126. // else
  127. $popular_tags = implode(', ', DB::select_values("id FROM tags WHERE tag_type = " . CONFIG::$tag_types['General'] . " ORDER BY post_count DESC LIMIT 8"));
  128. if ($popular_tags)
  129. $popular_tags = "AND pt.tag_id NOT IN (${popular_tags})";
  130. // end
  131. if ($type) {
  132. $type = (int)$type;
  133. $sql = "
  134. (SELECT name FROM tags WHERE id = pt.tag_id) AS tag, COUNT(*) AS count
  135. FROM posts_tags pt, tags t, posts p
  136. WHERE p.user_id = {$this->id}
  137. AND p.id = pt.post_id
  138. AND pt.tag_id = t.id
  139. {$popular_tags}
  140. AND t.tag_type = {$type}
  141. GROUP BY pt.tag_id
  142. ORDER BY count DESC
  143. LIMIT 6
  144. ";
  145. } else {
  146. $sql = "
  147. (SELECT name FROM tags WHERE id = pt.tag_id) AS tag, COUNT(*) AS count
  148. FROM posts_tags pt, posts p
  149. WHERE p.user_id = {$this->id}
  150. AND p.id = pt.post_id
  151. ${popular_tags}
  152. GROUP BY pt.tag_id
  153. ORDER BY count DESC
  154. LIMIT 6
  155. ";
  156. }
  157. $uploaded_tags = DB::select($sql);
  158. // if CONFIG["enable_caching"]
  159. // Cache.put("uploaded_tags/#{id}/#{type}", uploaded_tags, 1.day)
  160. // end
  161. return $uploaded_tags;
  162. }
  163. function voted_tags($options = array()) {
  164. $type = !empty($options['type']) ? $options['type'] : null;
  165. // if (CONFIG::enable_caching) {
  166. // favorite_tags = Cache.get("favorite_tags/#{id}/#{type}")
  167. // return favorite_tags unless favorite_tags == nil
  168. // }
  169. // if RAILS_ENV == "test"
  170. // # disable filtering in test mode to simplify tests
  171. // popular_tags = ""
  172. // else
  173. $popular_tags = implode(', ', DB::select_values("id FROM tags WHERE tag_type = " . CONFIG::$tag_types['General'] . " ORDER BY post_count DESC LIMIT 8"));
  174. if ($popular_tags)
  175. $popular_tags = "AND pt.tag_id NOT IN (${popular_tags})";
  176. // end
  177. if ($type) {
  178. $type = (int)$type;
  179. $sql = "
  180. (SELECT name FROM tags WHERE id = pt.tag_id) AS tag, SUM(v.score) AS sum
  181. FROM posts_tags pt, tags t, post_votes v
  182. WHERE v.user_id = {$this->id}
  183. AND v.post_id = pt.post_id
  184. AND pt.tag_id = t.id
  185. {$popular_tags}
  186. AND t.tag_type = {$type}
  187. GROUP BY pt.tag_id
  188. ORDER BY sum DESC
  189. LIMIT 6
  190. ";
  191. } else {
  192. $sql = "
  193. (SELECT name FROM tags WHERE id = pt.tag_id) AS tag, SUM(v.score) AS sum
  194. FROM posts_tags pt, post_votes v
  195. WHERE v.user_id = {$this->id}
  196. AND v.post_id = pt.post_id
  197. ${popular_tags}
  198. GROUP BY pt.tag_id
  199. ORDER BY sum DESC
  200. LIMIT 6
  201. ";
  202. }
  203. $favorite_tags = DB::select($sql);
  204. // if CONFIG["enable_caching"]
  205. // Cache.put("favorite_tags/#{id}/#{type}", favorite_tags, 1.day)
  206. // end
  207. return $favorite_tags;
  208. }
  209. function logout(){
  210. cookie_remove("login");
  211. cookie_remove("pass_hash");
  212. cookie_remove("user_info");
  213. cookie_remove("held_post_count");
  214. cookie_remove("show_advanced_editing");
  215. session_unset();
  216. }
  217. /* Post Methods { */
  218. function recent_uploaded_posts() {
  219. return Post::find_by_sql(array("SELECT p.* FROM posts p WHERE p.user_id = {$this->id} AND p.status <> 'deleted' ORDER BY p.id DESC LIMIT 6"));
  220. }
  221. function recent_favorite_posts() {
  222. return Post::find_by_sql(array("SELECT p.* FROM posts p, post_votes v WHERE p.id = v.post_id AND v.user_id = {$this->id} AND v.score = 3 AND p.status <> 'deleted' ORDER BY v.updated_at DESC LIMIT 6"));
  223. }
  224. function favorite_post_count($options = array()) {
  225. return DB::count("post_votes v WHERE v.user_id = {$this->id} AND v.score = 3");
  226. }
  227. function post_count() {
  228. return Post::count(array('conditions' => array("user_id = ? AND status = 'active'", $id)));
  229. // @post_count ||= Post.count(:conditions => ["user_id = ? AND status = 'active'", id])
  230. }
  231. function held_post_count() {
  232. // version = Cache.get("$cache_version").to_i
  233. // key = "held-post-count/v=#{version}/u=#{self.id}"
  234. // return Cache.get(key) {
  235. // Post.count(:conditions => ["user_id = ? AND is_held AND status <> 'deleted'", self.id])
  236. // }.to_i
  237. $count = Post::count(array('conditions' => array("user_id = ? AND is_held AND status <> 'deleted'", $this->id)));
  238. return $count;
  239. }
  240. /* } level methods { */
  241. function can_signup() {
  242. if (!CONFIG::enable_signups) {
  243. $this->record_errors->add('signups', 'are disabled');
  244. return false;
  245. }
  246. return true;
  247. }
  248. function set_role() {
  249. if (CONFIG::enable_account_email_activation)
  250. $this->level = CONFIG::$user_levels["Unactivated"];
  251. else
  252. $this->level = CONFIG::starting_level;
  253. $this->last_logged_in_at = gmd();
  254. }
  255. /* } Count methods { */
  256. function fast_count() {
  257. return DB::select_value("row_count FROM table_data WHERE name = 'users'");
  258. }
  259. function increment_count() {
  260. DB::update("table_data set row_count = row_count + 1 where name = 'users'");
  261. }
  262. function decrement_count() {
  263. DB::update("table_data set row_count = row_count - 1 where name = 'users'");
  264. }
  265. /* } Password methods { */
  266. function encrypt_password() {
  267. if ($this->password)
  268. $this->password_hash = md5($this->name.$this->password);
  269. }
  270. function reset_password() {
  271. $consonants = "bcdfghjklmnpqrstvqxyz";
  272. $vowels = "aeiou";
  273. $pass = "";
  274. foreach (range(1, 4) as $i) {
  275. $pass .= substr($consonants, rand(0, 20), 1);
  276. $pass .= substr($vowels, rand(0, 4), 1);
  277. }
  278. $pass .= rand(0, 100);
  279. DB::update("users SET password_hash = ? WHERE id = ?", md5($pass), $this->id);
  280. return $pass;
  281. }
  282. /* } Avatar methods { */
  283. static function clear_avatars($post_id) {
  284. return DB::update("users SET avatar_post_id = NULL WHERE avatar_post_id = ?", $post_id);
  285. }
  286. function avatar_url() {
  287. return CONFIG::url_base . "/data/avatars/{$this->id}.jpg";
  288. }
  289. function has_avatar() {
  290. return !empty($this->avatar_post_id);
  291. }
  292. function avatar_path() {
  293. return ROOT . "public/data/avatars/" . $this->id . ".jpg";
  294. }
  295. function set_avatar($params) {
  296. $post = Post::find($params['post_id']);
  297. if (!$post->can_be_seen_by($this)) {
  298. $this->record_errors->add('access', "denied");
  299. return false;
  300. }
  301. // vde($params);
  302. if ($params['top'] < 0 or $params['top'] > 1 or
  303. $params['bottom'] < 0 or $params['bottom'] > 1 or
  304. $params['left'] < 0 or $params['left'] > 1 or
  305. $params['right'] < 0 or $params['right'] > 1 or
  306. $params['top'] >= $params['bottom'] or
  307. $params['left'] >= $params['right'])
  308. {
  309. $this->record_errors->add('parameter', "error");
  310. return false;
  311. }
  312. $tempfile_path = ROOT . "public/data/" . $this->id . ".avatar.jpg";
  313. $use_sample = $post->has_sample();
  314. if ($use_sample) {
  315. $image_path = $post->sample_path();
  316. $image_ext = "jpg";
  317. $size = $this->reduce_and_crop($post->sample_width, $post->sample_height, $params);
  318. # If we're cropping from a very small region in the sample, use the full
  319. # image instead, to get a higher quality image.
  320. if (($size['crop_bottom'] - $size['crop_top'] < CONFIG::avatar_max_height) or
  321. ($size['crop_right'] - $size['crop_left'] < CONFIG::avatar_max_width))
  322. $use_sample = false;
  323. }
  324. if (!$use_sample) {
  325. $image_path = $post->file_path();
  326. $image_ext = $post->file_ext;
  327. $size = $this->reduce_and_crop($post->width, $post->height, $params);
  328. }
  329. try {
  330. Danbooru::resize($image_ext, $image_path, $tempfile_path, $size, 95);
  331. } catch (Exception $x) {
  332. if (file_exists($tempfile_path))
  333. unlink($tempfile_path);
  334. $this->record_errors->add("avatar", "couldn't be generated (" . $x->getMessage() . ")");
  335. return false;
  336. }
  337. rename($tempfile_path, $this->avatar_path());
  338. chmod($this->avatar_path(), 0775);
  339. $this->update_attributes(array(
  340. 'avatar_post_id' => $params['post_id'],
  341. 'avatar_top' => $params['top'],
  342. 'avatar_bottom' => $params['bottom'],
  343. 'avatar_left' => $params['left'],
  344. 'avatar_right' => $params['right'],
  345. 'avatar_width' => $size['width'],
  346. 'avatar_height' => $size['height'],
  347. 'avatar_timestamp' => gmd()
  348. ));
  349. return true;
  350. }
  351. function reduce_and_crop($image_width, $image_height, $params) {
  352. $cropped_image_width = $image_width * ($params['right'] - $params['left']);
  353. $cropped_image_height = $image_height * ($params['bottom'] - $params['top']);
  354. $size = Danbooru::reduce_to(array('width' => $cropped_image_width, 'height' => $cropped_image_height), array('width' => CONFIG::avatar_max_width, 'height' => CONFIG::avatar_max_height), 1, true);
  355. $size['crop_top'] = $image_height * $params['top'];
  356. $size['crop_bottom'] = $image_height * $params['bottom'];
  357. $size['crop_left'] = $image_width * $params['left'];
  358. $size['crop_right'] = $image_width * $params['right'];
  359. return $size;
  360. }
  361. }
  362. ?>