Qurl.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. App::uses('ToolsAppModel', 'Tools.Model');
  3. App::uses('Hash', 'Utility');
  4. /**
  5. * Manage Quick Urls
  6. *
  7. * @author Mark Scherer
  8. * @license MIT
  9. */
  10. class Qurl extends ToolsAppModel {
  11. public $displayField = 'key';
  12. public $scaffoldSkipFields = array('note', 'key', 'content');
  13. public $order = array('Qurl.created' => 'DESC');
  14. protected $defaultLength = 22;
  15. protected $validity = YEAR;
  16. public $validate = array(
  17. 'key' => array(
  18. 'notEmpty' => array(
  19. 'rule' => array('notEmpty'),
  20. 'message' => 'valErrMandatoryField',
  21. 'last' => true,
  22. ),
  23. 'isUnique' => array(
  24. 'rule' => array('isUnique'),
  25. 'message' => 'valErrQurlKeyExists',
  26. ),
  27. ),
  28. 'url' => array(
  29. 'notEmpty' => array(
  30. 'rule' => array('notEmpty'),
  31. 'message' => 'valErrMandatoryField',
  32. 'last' => true
  33. ),
  34. 'validateUrl' => array(
  35. 'rule' => array('validateUrl', array('deep' => false, 'sameDomain' => true, 'autoComplete' => true)),
  36. 'message' => 'valErrInvalidQurlUrl',
  37. 'last' => true
  38. )
  39. ),
  40. 'note' => array(
  41. ),
  42. 'comment' => array(
  43. ),
  44. 'used' => array(
  45. 'numeric' => array(
  46. 'rule' => array('numeric'),
  47. 'message' => 'valErrMandatoryField',
  48. ),
  49. ),
  50. );
  51. public function beforeValidate($options = array()) {
  52. parent::beforeValidate($options);
  53. if (isset($this->data[$this->alias]['key']) && empty($this->data[$this->alias]['key'])) {
  54. $length = null;
  55. if (!empty($this->data[$this->alias]['key_length'])) {
  56. $length = $this->data[$this->alias]['key_length'];
  57. }
  58. $this->data[$this->alias]['key'] = $this->generateKey($length);
  59. }
  60. return true;
  61. }
  62. public function beforeSave($options = array()) {
  63. parent::beforeSave($options);
  64. if (isset($this->data[$this->alias]['content'])) {
  65. $this->data[$this->alias]['content'] = serialize($this->data[$this->alias]['content']);
  66. }
  67. return true;
  68. }
  69. /**
  70. * Qurl::translate()
  71. *
  72. * @param mixed $key
  73. * @return array
  74. */
  75. public function translate($key) {
  76. $res = $this->find('first', array('conditions' => array($this->alias . '.key' => $key, $this->alias . '.active' => 1)));
  77. if (!$res) {
  78. return false;
  79. }
  80. if ($res[$this->alias]['content']) {
  81. $res[$this->alias]['content'] = unserialize($res[$this->alias]['content']);
  82. } else {
  83. $res[$this->alias]['content'] = array();
  84. }
  85. return $res;
  86. }
  87. /**
  88. * Form the access url by key
  89. *
  90. * @param string $key
  91. * @return string Url (absolute)
  92. */
  93. public static function urlByKey($key, $title = null, $slugTitle = true) {
  94. if ($title && $slugTitle) {
  95. $title = Inflector::slug($title, '-');
  96. }
  97. return Router::url(array('admin' => false, 'plugin' => 'tools', 'controller' => 'qurls', 'action' => 'go', $key, $title), true);
  98. }
  99. /**
  100. * Makes an absolute url string ready to input anywhere.
  101. * Uses generate() internally to get the key.
  102. *
  103. * @param mixed $url
  104. * @return string Url (absolute)
  105. */
  106. public function url($url, $data = array()) {
  107. $key = $this->generate($url, $data);
  108. if (!$key) {
  109. return false;
  110. }
  111. return $this->urlByKey($key);
  112. }
  113. /**
  114. * Generates a Qurl key
  115. *
  116. * @param mixed $url
  117. * @param string $uid
  118. * @return string Key
  119. */
  120. public function generate($url, $data = array()) {
  121. $url = Router::url($url, true);
  122. $content = array_merge($data, array('url' => $url));
  123. if (!isset($content['key'])) {
  124. $content['key'] = '';
  125. }
  126. $res = $this->save($content);
  127. if (!$res) {
  128. return false;
  129. }
  130. return $res[$this->alias]['key'];
  131. }
  132. /**
  133. * Sets Key to "used" (only once!) - directly by ID
  134. *
  135. * @param id of key to spend: necessary
  136. * @return bool Success
  137. */
  138. public function markAsUsed($id = null) {
  139. if (empty($id)) {
  140. return false;
  141. }
  142. //$this->id = $id;
  143. if ($this->updateAll(array($this->alias . '.used' => $this->alias . '.used + 1', $this->alias . '.last_used' => '"' . date(FORMAT_DB_DATETIME) . '"'), array($this->alias . '.id' => $id))) {
  144. return true;
  145. }
  146. return false;
  147. }
  148. /**
  149. * Remove old/invalid keys
  150. * does not remove recently used ones (for proper feedback)!
  151. *
  152. * @return bool success
  153. */
  154. public function garbageCollector() {
  155. $conditions = array(
  156. $this->alias . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity),
  157. );
  158. return $this->deleteAll($conditions, false);
  159. }
  160. /**
  161. * Get admin stats
  162. *
  163. * @return array
  164. */
  165. public function stats() {
  166. $keys = array();
  167. $keys['unused_valid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 0, $this->alias . '.created >=' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
  168. $keys['used_valid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 1, $this->alias . '.created >=' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
  169. $keys['unused_invalid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 0, $this->alias . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
  170. $keys['used_invalid'] = $this->find('count', array('conditions' => array($this->alias . '.used' => 1, $this->alias . '.created <' => date(FORMAT_DB_DATETIME, time() - $this->validity))));
  171. $urls = $this->find('all', array('conditions' => array(), 'fields' => array('DISTINCT url')));
  172. $keys['urls'] = !empty($urls) ? Hash::extract('{n}.' . $this->alias . '.url', $urls) : array();
  173. return $keys;
  174. }
  175. /**
  176. * Generate key
  177. *
  178. * @param length (defaults to defaultLength)
  179. * @return string codekey
  180. */
  181. public function generateKey($length = null) {
  182. if (empty($length)) {
  183. $length = $this->defaultLength;
  184. }
  185. App::uses('RandomLib', 'Tools.Lib');
  186. return RandomLib::generatePassword($length);
  187. }
  188. }