Merge pull request #13502 from colemanw/shortcode
[civicrm-core.git] / CRM / Utils / Sort.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * Base class to provide generic sort functionality.
31 *
32 * Note that some ideas have been borrowed from the drupal tablesort.inc code.
33 *
34 * Also note that since the Pager and Sort class are similar, do match the function names
35 * if introducing additional functionality
36 *
37 * @package CRM
38 * @copyright CiviCRM LLC (c) 2004-2019
39 */
40 class CRM_Utils_Sort {
41
42 /**
43 * Constants to determine what direction each variable
44 * is to be sorted
45 *
46 * @var int
47 */
48 const ASCENDING = 1, DESCENDING = 2, DONTCARE = 4,
49
50 /**
51 * The name for the sort GET/POST param
52 *
53 * @var string
54 */
55 SORT_ID = 'crmSID', SORT_DIRECTION = 'crmSortDirection', SORT_ORDER = 'crmSortOrder';
56
57 /**
58 * Name of the sort function. Used to isolate session variables
59 * @var string
60 */
61 protected $_name;
62
63 /**
64 * Array of variables that influence the query
65 *
66 * @var array
67 */
68 public $_vars;
69
70 /**
71 * The newly formulated base url to be used as links
72 * for various table elements
73 *
74 * @var string
75 */
76 protected $_link;
77
78 /**
79 * What's the name of the sort variable in a REQUEST
80 *
81 * @var string
82 */
83 protected $_urlVar;
84
85 /**
86 * What variable are we currently sorting on
87 *
88 * @var string
89 */
90 protected $_currentSortID;
91
92 /**
93 * What direction are we sorting on
94 *
95 * @var string
96 */
97 protected $_currentSortDirection;
98
99 /**
100 * The output generated for the current form
101 *
102 * @var array
103 */
104 public $_response;
105
106 /**
107 * The constructor takes an assoc array
108 * key names of variable (which should be the same as the column name)
109 * value: ascending or descending
110 *
111 * @param mixed $vars
112 * Assoc array as described above.
113 * @param string $defaultSortOrder
114 * Order to sort.
115 *
116 * @return \CRM_Utils_Sort
117 */
118 public function __construct(&$vars, $defaultSortOrder = NULL) {
119 $this->_vars = array();
120 $this->_response = array();
121
122 foreach ($vars as $weight => $value) {
123 $this->_vars[$weight] = array(
124 'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnNameOrAlias'),
125 'direction' => CRM_Utils_Array::value('direction', $value),
126 'title' => $value['name'],
127 );
128 }
129
130 $this->_currentSortID = 1;
131 if (isset($this->_vars[$this->_currentSortID])) {
132 $this->_currentSortDirection = $this->_vars[$this->_currentSortID]['direction'];
133 }
134 $this->_urlVar = self::SORT_ID;
135 $this->_link = CRM_Utils_System::makeURL($this->_urlVar, TRUE);
136
137 $this->initialize($defaultSortOrder);
138 }
139
140 /**
141 * Function returns the string for the order by clause.
142 *
143 * @return string
144 * the order by clause
145 */
146 public function orderBy() {
147 if (empty($this->_vars[$this->_currentSortID])) {
148 return '';
149 }
150
151 if ($this->_vars[$this->_currentSortID]['direction'] == self::ASCENDING ||
152 $this->_vars[$this->_currentSortID]['direction'] == self::DONTCARE
153 ) {
154 $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
155 return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' asc';
156 }
157 else {
158 $this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
159 return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' desc';
160 }
161 }
162
163 /**
164 * Create the sortID string to be used in the GET param.
165 *
166 * @param int $index
167 * The field index.
168 * @param int $dir
169 * The direction of the sort.
170 *
171 * @return string
172 * the string to append to the url
173 */
174 public static function sortIDValue($index, $dir) {
175 return ($dir == self::DESCENDING) ? $index . '_d' : $index . '_u';
176 }
177
178 /**
179 * Init the sort ID values in the object.
180 *
181 * @param string $defaultSortOrder
182 * The sort order to use by default.
183 */
184 public function initSortID($defaultSortOrder) {
185 $url = CRM_Utils_Array::value(self::SORT_ID, $_GET, $defaultSortOrder);
186
187 if (empty($url)) {
188 return;
189 }
190
191 list($current, $direction) = explode('_', $url);
192
193 // if current is weird and does not exist in the vars array, skip
194 if (!array_key_exists($current, $this->_vars)) {
195 return;
196 }
197
198 if ($direction == 'u') {
199 $direction = self::ASCENDING;
200 }
201 elseif ($direction == 'd') {
202 $direction = self::DESCENDING;
203 }
204 else {
205 $direction = self::DONTCARE;
206 }
207
208 $this->_currentSortID = $current;
209 $this->_currentSortDirection = $direction;
210 $this->_vars[$current]['direction'] = $direction;
211 }
212
213 /**
214 * Init the object.
215 *
216 * @param string $defaultSortOrder
217 * The sort order to use by default.
218 */
219 public function initialize($defaultSortOrder) {
220 $this->initSortID($defaultSortOrder);
221
222 $this->_response = array();
223
224 $current = $this->_currentSortID;
225 foreach ($this->_vars as $index => $item) {
226 $name = $item['name'];
227 $this->_response[$name] = array();
228
229 $newDirection = ($item['direction'] == self::ASCENDING) ? self::DESCENDING : self::ASCENDING;
230
231 if ($current == $index) {
232 if ($item['direction'] == self::ASCENDING) {
233 $class = 'sorting_asc';
234 }
235 else {
236 $class = 'sorting_desc';
237 }
238 }
239 else {
240 $class = 'sorting';
241 }
242
243 $this->_response[$name]['link'] = '<a href="' . $this->_link . $this->sortIDValue($index, $newDirection) . '" class="' . $class . '">' . $item['title'] . '</a>';
244 }
245 }
246
247 /**
248 * Getter for currentSortID.
249 *
250 * @return int
251 * returns of the current sort id
252 */
253 public function getCurrentSortID() {
254 return $this->_currentSortID;
255 }
256
257 /**
258 * Getter for currentSortDirection.
259 *
260 * @return int
261 * returns of the current sort direction
262 */
263 public function getCurrentSortDirection() {
264 return $this->_currentSortDirection;
265 }
266
267 /**
268 * Universal callback function for sorting by weight, id, title or name
269 *
270 * @param $a
271 * @param $b
272 *
273 * @return int
274 * (-1 or 1)
275 */
276 public static function cmpFunc($a, $b) {
277 $cmp_order = array('weight', 'id', 'title', 'name');
278 foreach ($cmp_order as $attribute) {
279 if (isset($a[$attribute]) && isset($b[$attribute])) {
280 if ($a[$attribute] < $b[$attribute]) {
281 return -1;
282 }
283 elseif ($a[$attribute] > $b[$attribute]) {
284 return 1;
285 } // else: $a and $b are equal wrt to this attribute, try next...
286 }
287 }
288 // if we get here, $a and $b are equal for all we know
289 // however, as I understand we don't want equality here:
290 return -1;
291 }
292
293 }