Qurl.php 5.3 KB

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