Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 423 |
|
0.00% |
0 / 58 |
CRAP | |
0.00% |
0 / 1 |
| zbsDAL_ObjectLayer | |
0.00% |
0 / 423 |
|
0.00% |
0 / 58 |
53130 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
210 | |||
| objTableName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| objType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| objModel | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| linkedToObjectTypes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| is_included_in_templating | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| objModelIncCustomFields | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
240 | |||
| hasCustomFields | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
90 | |||
| getCustomFields | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
132 | |||
| includesAddressFields | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| DAL | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| objFieldCSV | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
42 | |||
| get | |
0.00% |
0 / 41 |
|
0.00% |
0 / 1 |
182 | |||
| generateFieldsGlobalArr | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
380 | |||
| getDAL1toDAL3ConversionMatrix | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
| getSingle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getSingleCustomFields | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
42 | |||
| getIDList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getAll | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getFullCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getOwner | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
| setOwner | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
| updateMeta | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
| tidy | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
56 | |||
| tidyAddCustomFields | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
342 | |||
| wpdbChecks | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
110 | |||
| verifyUniqueValues | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
342 | |||
| verifyNonEmptyValues | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
156 | |||
| db_ready_obj | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
56 | |||
| addUpdateObjectLinks | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
72 | |||
| lazyTable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| lazyTidy | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| lazyTidyGeneric | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| space | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| spaceAnd | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| spaceWhere | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| delimiterIf | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| stripSlashes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| decodeIfJSON | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| build_csv | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildWhereStr | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildWheres | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildSort | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildPaging | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildWPMetaQueryWhere | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getTypeStr | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| prepare | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| catchSQLError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| format_fullname | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| format_name_etc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| format_address | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| makeSlug | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| makeSlugCleanStr | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| ownershipQueryVars | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| ownershipSQL | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| addUpdateCustomField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| add_name_clash_suffix_if_needed | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
| fix_name_clash_if_needed | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | /* |
| 3 | * Jetpack CRM |
| 4 | * https://jetpackcrm.com |
| 5 | * V3.0+ |
| 6 | * |
| 7 | * Copyright 2020 Automattic |
| 8 | * |
| 9 | * Date: 14/01/19 |
| 10 | */ |
| 11 | |
| 12 | defined( 'ZEROBSCRM_PATH' ) || exit( 0 ); |
| 13 | |
| 14 | /** |
| 15 | * ZBS DAL >> Object Class |
| 16 | * |
| 17 | * @author Woody Hayday <hello@jetpackcrm.com> |
| 18 | * @version 2.0 |
| 19 | * @access public |
| 20 | * @see https://jetpackcrm.com/kb |
| 21 | */ |
| 22 | class zbsDAL_ObjectLayer { |
| 23 | |
| 24 | protected $objectType = -1; // e.g. ZBS_TYPE_CONTACT |
| 25 | protected $objectModel = -1; // array('DBFIELD(e.g. zbs_owner'=>array('Local field (e.g. owner)','format (int)')) |
| 26 | // formats: |
| 27 | // int = (int) |
| 28 | // uts = uts + converted to locale date _datestr |
| 29 | // |
| 30 | protected $objectTableName = -1; |
| 31 | protected $objectFieldCSV = -1; // assumes model wont change mid-load :) |
| 32 | protected $include_in_templating = false; // if true, object types fields will be accessible in templating |
| 33 | |
| 34 | // hardtyped list of types this object type is commonly linked to |
| 35 | // Note that as of 4.1.1 this mechanism is only used via DAL3.Export to know what typical links to look for, it is not a hard rule, or currently respected anywhere else. |
| 36 | // e.g. Invoice object type may be commonly linked to 'contact' or 'company' object types |
| 37 | protected $linkedToObjectTypes = array(); |
| 38 | |
| 39 | /** This field is used to store name clashes so we can change custom field names |
| 40 | * with name clashes in function `fix_name_clash_if_needed()` |
| 41 | * See: https://github.com/Automattic/zero-bs-crm/issues/3477 |
| 42 | * |
| 43 | * @var array |
| 44 | */ |
| 45 | protected $name_clashes_temp_fix = array(); |
| 46 | |
| 47 | /** Suffix used to fix name clashes |
| 48 | * See: https://github.com/Automattic/zero-bs-crm/issues/3477 |
| 49 | */ |
| 50 | protected const NAME_CLASH_FIX_SUFFIX = '_zbs-name-clash-tmp-fix'; |
| 51 | |
| 52 | /** |
| 53 | * Prefix used in database table columns, etc. |
| 54 | * |
| 55 | * @var string |
| 56 | */ |
| 57 | protected $objectDBPrefix; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase |
| 58 | |
| 59 | /** |
| 60 | * Whether object has addresses or not. |
| 61 | * |
| 62 | * @var boolean |
| 63 | */ |
| 64 | protected $objectIncludesAddresses; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase |
| 65 | |
| 66 | function __construct( $args = array() ) { |
| 67 | |
| 68 | #} =========== LOAD ARGS ============== |
| 69 | $defaultArgs = array( |
| 70 | |
| 71 | // 'tag' => false, |
| 72 | |
| 73 | ); |
| 74 | foreach ( $defaultArgs as $argK => $argV ) { |
| 75 | $this->$argK = $argV; |
| 76 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 77 | if ( is_array( $args[ $argK ] ) ) { |
| 78 | $newData = $this->$argK; |
| 79 | if ( ! is_array( $newData ) ) { |
| 80 | $newData = array(); |
| 81 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 82 | $newData[ $subK ] = $subV; |
| 83 | }$this->$argK = $newData; |
| 84 | } else { |
| 85 | $this->$argK = $args[ $argK ]; } |
| 86 | } |
| 87 | } |
| 88 | #} =========== / LOAD ARGS ============= |
| 89 | |
| 90 | // check objectModel + err if not legit |
| 91 | |
| 92 | // ==== AUTO TRANSLATION |
| 93 | // Any labels passed with $objectModel need passing through translation :) |
| 94 | if ( is_array( $this->objectModel ) ) { |
| 95 | foreach ( $this->objectModel as $key => $fieldArr ) { |
| 96 | |
| 97 | if ( isset( $fieldArr['label'] ) ) { |
| 98 | $fieldArr['label'] = __( $fieldArr['label'], 'zero-bs-crm' ); |
| 99 | } |
| 100 | if ( isset( $fieldArr['placeholder'] ) ) { |
| 101 | $fieldArr['placeholder'] = __( $fieldArr['placeholder'], 'zero-bs-crm' ); |
| 102 | } |
| 103 | if ( isset( $fieldArr['options'] ) && is_array( $fieldArr['options'] ) ) { |
| 104 | |
| 105 | $newOptions = array(); |
| 106 | foreach ( $fieldArr['options'] as $o ) { |
| 107 | $newOptions[] = __( $o, 'zero-bs-crm' ); |
| 108 | } |
| 109 | $fieldArr['options'] = $newOptions; |
| 110 | unset( $newOptions ); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // return core vars |
| 117 | public function objTableName() { |
| 118 | return $this->objectTableName; |
| 119 | } |
| 120 | public function objType() { |
| 121 | return $this->objectType; |
| 122 | } |
| 123 | public function objModel( $appendCustomFields = false ) { |
| 124 | return $this->objectModel; |
| 125 | } |
| 126 | public function linkedToObjectTypes() { |
| 127 | return $this->linkedToObjectTypes; |
| 128 | } |
| 129 | public function is_included_in_templating() { |
| 130 | return $this->include_in_templating; |
| 131 | } |
| 132 | |
| 133 | // return the objModel with additional custom fields as if they were db-ready fields |
| 134 | // Note: This is a bridging function currently only used in DAL3.Exports.php, |
| 135 | // ... a refactoring of the dbmodel+customfields+globalfieldarrays is necessary pre v4.0 |
| 136 | // ... in mean time, avoid usage in the initial field setup linkages |
| 137 | // |
| 138 | // Note: Also the format of the custom field arr will differ from the objmodel field arr's |
| 139 | // ... to distinguish, look for attribute 'custom-field' |
| 140 | // ... but probably best to generally avoid usage of this function until reconciled the above note. |
| 141 | // see gh-253 |
| 142 | public function objModelIncCustomFields() { |
| 143 | |
| 144 | global $zbs; |
| 145 | |
| 146 | $customFields = false; |
| 147 | $model = $this->objectModel; |
| 148 | |
| 149 | // turn ZBS_TYPE_CONTACT (1) into "contact" |
| 150 | $typeStr = $this->DAL()->objTypeKey( $this->objectType ); |
| 151 | |
| 152 | // Direct retrieval v3+ |
| 153 | if ( ! empty( $typeStr ) ) { |
| 154 | $customFields = $zbs->DAL->setting( 'customfields_' . $typeStr, array() ); |
| 155 | } |
| 156 | |
| 157 | // if is an obj which has custom field capacity: |
| 158 | if ( isset( $customFields ) && is_array( $customFields ) ) { |
| 159 | |
| 160 | // add to model |
| 161 | foreach ( $customFields as $fieldKey => $field ) { |
| 162 | |
| 163 | // Unpacks csv options and sets 'custom-field' attr |
| 164 | // Adds it to arr |
| 165 | // ignores potential collisions (e.g. custom field with key 'status'), these should be blocked by UI |
| 166 | $model[ $fieldKey ] = zeroBSCRM_customFields_processCustomField( $field ); |
| 167 | |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | // if obj also has addresses, check for address custom fields |
| 172 | // Adapted from DAL3.Fields.php |
| 173 | // see gh-253 |
| 174 | if ( $this->includesAddressFields() ) { |
| 175 | |
| 176 | #} Retrieve |
| 177 | $addrCustomFields = $zbs->DAL->getActiveCustomFields( array( 'objtypeid' => ZBS_TYPE_ADDRESS ) ); |
| 178 | |
| 179 | #} Addresses |
| 180 | if ( is_array( $addrCustomFields ) && count( $addrCustomFields ) > 0 ) { |
| 181 | |
| 182 | $cfIndx = 1; |
| 183 | foreach ( $addrCustomFields as $fieldKey => $field ) { |
| 184 | |
| 185 | // unpacks csv options and sets 'custom-field' attr |
| 186 | $fieldO = zeroBSCRM_customFields_processCustomField( $field ); |
| 187 | |
| 188 | // splice them in to the end of the e.g. 'second address' group |
| 189 | |
| 190 | // Addr1 |
| 191 | |
| 192 | // for this specifically, we also add '[area]' |
| 193 | $fieldO['area'] = 'Main Address'; |
| 194 | |
| 195 | // find index |
| 196 | $mainAddrIndx = -1; |
| 197 | $i = 0; foreach ( $model as $k => $f ) { |
| 198 | if ( isset( $f['area'] ) && $f['area'] == 'Main Address' ) { |
| 199 | $mainAddrIndx = $i; |
| 200 | } |
| 201 | ++$i; |
| 202 | } |
| 203 | |
| 204 | // splice |
| 205 | ++$mainAddrIndx; // req |
| 206 | $model = array_merge( |
| 207 | array_slice( $model, 0, $mainAddrIndx, true ), |
| 208 | array( 'addr_' . $fieldKey => $fieldO ), |
| 209 | array_slice( $model, $mainAddrIndx, null, true ) |
| 210 | ); |
| 211 | |
| 212 | // Addr2 |
| 213 | |
| 214 | // change area |
| 215 | $fieldO['area'] = 'Second Address'; |
| 216 | |
| 217 | // find index |
| 218 | $secAddrIndx = -1; |
| 219 | $i = 0; foreach ( $model as $k => $f ) { |
| 220 | if ( isset( $f['area'] ) && $f['area'] == 'Second Address' ) { |
| 221 | $secAddrIndx = $i; |
| 222 | } |
| 223 | ++$i; |
| 224 | } |
| 225 | |
| 226 | // splice |
| 227 | ++$secAddrIndx; // req |
| 228 | $model = array_merge( |
| 229 | array_slice( $model, 0, $secAddrIndx, true ), |
| 230 | array( 'secaddr_' . $fieldKey => $fieldO ), |
| 231 | array_slice( $model, $secAddrIndx, null, true ) |
| 232 | ); |
| 233 | |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | return $model; |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * returns bool depending on whether object has custom fields setup |
| 243 | * |
| 244 | * @return bool |
| 245 | */ |
| 246 | public function hasCustomFields( $includeHidden = false ) { |
| 247 | |
| 248 | global $zbs; |
| 249 | |
| 250 | $fieldsToHide = array(); |
| 251 | |
| 252 | // turn ZBS_TYPE_CONTACT (1) into "contact" |
| 253 | $typeStr = $this->DAL()->objTypeKey( $this->objectType ); |
| 254 | |
| 255 | // any to hide? |
| 256 | if ( ! $includeHidden ) { |
| 257 | |
| 258 | $fieldHideOverrides = $zbs->settings->get( 'fieldhides' ); |
| 259 | if ( isset( $fieldHideOverrides[ $typeStr ] ) ) { |
| 260 | $fieldsToHide = $fieldHideOverrides[ $typeStr ]; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | // Direct retrieval v3+ |
| 265 | if ( ! empty( $typeStr ) ) { |
| 266 | |
| 267 | $customFields = $zbs->DAL->setting( 'customfields_' . $typeStr, array() ); |
| 268 | |
| 269 | // got custom fields? |
| 270 | if ( isset( $customFields ) && is_array( $customFields ) ) { |
| 271 | |
| 272 | // cycle through custom fields |
| 273 | foreach ( $customFields as $fieldKey => $field ) { |
| 274 | |
| 275 | // hidden? |
| 276 | if ( $includeHidden || ! in_array( $fieldKey, $fieldsToHide ) ) { |
| 277 | |
| 278 | return true; |
| 279 | |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | /** |
| 289 | * returns custom fields for an object |
| 290 | * .. optionally excluding hidden (from fieldsort page) |
| 291 | * |
| 292 | * @return array custom fields |
| 293 | */ |
| 294 | public function getCustomFields( $includeHidden = false ) { |
| 295 | |
| 296 | global $zbs; |
| 297 | |
| 298 | $returnCustomFields = array(); |
| 299 | $fieldsToHide = array(); |
| 300 | |
| 301 | // turn ZBS_TYPE_CONTACT (1) into "contact" |
| 302 | $typeStr = $this->DAL()->objTypeKey( $this->objectType ); |
| 303 | |
| 304 | // any to hide? |
| 305 | if ( ! $includeHidden ) { |
| 306 | |
| 307 | $fieldHideOverrides = $zbs->settings->get( 'fieldhides' ); |
| 308 | if ( isset( $fieldHideOverrides[ $typeStr ] ) ) { |
| 309 | $fieldsToHide = $fieldHideOverrides[ $typeStr ]; |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | // Direct retrieval v3+ |
| 314 | if ( ! empty( $typeStr ) ) { |
| 315 | |
| 316 | $customFields = $zbs->DAL->setting( 'customfields_' . $typeStr, array() ); |
| 317 | |
| 318 | // got custom fields? |
| 319 | if ( isset( $customFields ) && is_array( $customFields ) ) { |
| 320 | |
| 321 | // cycle through custom fields |
| 322 | foreach ( $customFields as $fieldKey => $field ) { |
| 323 | |
| 324 | // hidden? |
| 325 | if ( $includeHidden || ! $fieldsToHide || ( is_array( $fieldsToHide ) && ! in_array( $fieldKey, $fieldsToHide ) ) ) { |
| 326 | |
| 327 | // Unpacks csv options and sets 'custom-field' attr |
| 328 | // Adds it to arr |
| 329 | $returnCustomFields[ $fieldKey ] = $field; |
| 330 | |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | return $returnCustomFields; |
| 337 | } |
| 338 | |
| 339 | public function includesAddressFields() { |
| 340 | |
| 341 | if ( isset( $this->objectIncludesAddresses ) ) { |
| 342 | return true; |
| 343 | } |
| 344 | |
| 345 | return false; |
| 346 | } |
| 347 | |
| 348 | public function DAL() { |
| 349 | |
| 350 | // hmm this is reference to a kind of 'parent' but not parent class. not sure best reference here. |
| 351 | // (to get back to $zbs->DAL) |
| 352 | // ... this allows us to centralise the reference in all children classes, at least, but probs a more oop logical way to do this |
| 353 | // is mostly helpers like buildWhere etc. but also other DAL funcs. |
| 354 | global $zbs; |
| 355 | return $zbs->DAL; |
| 356 | } |
| 357 | |
| 358 | // internal sql helpers |
| 359 | private function objFieldCSV() { |
| 360 | |
| 361 | // assumes model wont change :) |
| 362 | if ( $this->objectFieldCSV == -1 || $this->objectFieldCSV == '' ) { |
| 363 | |
| 364 | $x = ''; if ( is_array( $this->objectModel ) ) { |
| 365 | foreach ( $this->objectModel as $k => $v ) { |
| 366 | if ( ! empty( $x ) ) { |
| 367 | $x .= ','; |
| 368 | } |
| 369 | $x .= $k; |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | $this->objectFieldCSV = $x; |
| 374 | |
| 375 | } |
| 376 | |
| 377 | return $this->objectFieldCSV; |
| 378 | } |
| 379 | |
| 380 | // basic get any (first 10 paged) |
| 381 | public function get( $args = array() ) { |
| 382 | |
| 383 | // hmm this is reference to a kind of 'parent' but not parent class. not sure best reference here. |
| 384 | // (to get back to $zbs->DAL) |
| 385 | global $zbs; |
| 386 | |
| 387 | #} =========== LOAD ARGS ============== |
| 388 | $defaultArgs = array( |
| 389 | |
| 390 | 'sortByField' => 'ID', |
| 391 | 'sortOrder' => 'ASC', |
| 392 | 'page' => 0, |
| 393 | 'perPage' => 10, |
| 394 | |
| 395 | ); |
| 396 | foreach ( $defaultArgs as $argK => $argV ) { |
| 397 | $$argK = $argV; |
| 398 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 399 | if ( is_array( $args[ $argK ] ) ) { |
| 400 | $newData = $$argK; |
| 401 | if ( ! is_array( $newData ) ) { |
| 402 | $newData = array(); |
| 403 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 404 | $newData[ $subK ] = $subV; |
| 405 | }$$argK = $newData; |
| 406 | } else { |
| 407 | $$argK = $args[ $argK ]; } |
| 408 | } |
| 409 | } |
| 410 | #} =========== / LOAD ARGS ============= |
| 411 | |
| 412 | #} ========== CHECK FIELDS ============ |
| 413 | |
| 414 | // always ignore owner for now (settings global) |
| 415 | $ignoreowner = true; |
| 416 | |
| 417 | #} ========= / CHECK FIELDS =========== |
| 418 | |
| 419 | global $ZBSCRM_t, $wpdb; |
| 420 | $wheres = array( 'direct' => array() ); |
| 421 | $whereStr = ''; |
| 422 | $additionalWhere = ''; |
| 423 | $params = array(); |
| 424 | $res = array(); |
| 425 | |
| 426 | #} Build query |
| 427 | $query = 'SELECT ' . $this->objFieldCSV() . ' FROM ' . $this->objectTableName; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase |
| 428 | |
| 429 | #} ============= WHERE ================ |
| 430 | |
| 431 | #} ============ / WHERE =============== |
| 432 | |
| 433 | #} Build out any WHERE clauses |
| 434 | $wheresArr = $zbs->DAL2->buildWheres( $wheres, $whereStr, $params ); |
| 435 | $whereStr = $wheresArr['where']; |
| 436 | $params = $params + $wheresArr['params']; |
| 437 | #} / Build WHERE |
| 438 | |
| 439 | #} Ownership v1.0 - the following adds SITE + TEAM checks, and (optionally), owner |
| 440 | $params = array_merge( $params, $zbs->DAL->tools->ownershipQueryVars( $ignoreowner ) ); // merges in any req. |
| 441 | $ownQ = $zbs->DAL->ownershipSQL( $ignoreowner ); |
| 442 | if ( ! empty( $ownQ ) ) { |
| 443 | $additionalWhere = $zbs->DAL->spaceAnd( $additionalWhere ) . $ownQ; // adds str to query |
| 444 | } |
| 445 | #} / Ownership |
| 446 | |
| 447 | #} Append to sql (this also automatically deals with sortby and paging) |
| 448 | $query .= $zbs->DAL->buildWhereStr( $whereStr, $additionalWhere ) . $zbs->DAL->buildSort( $sortByField, $sortOrder ) . $zbs->DAL->buildPaging( $page, $perPage ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable |
| 449 | |
| 450 | try { |
| 451 | |
| 452 | #} Prep & run query |
| 453 | $queryObj = $zbs->DAL->prepare( $query, $params ); |
| 454 | $potentialRes = $zbs->DAL->get_results( $queryObj, OBJECT ); |
| 455 | |
| 456 | } catch ( Exception $e ) { |
| 457 | |
| 458 | #} General SQL Err |
| 459 | $zbs->DAL->catchSQLError( $e ); |
| 460 | |
| 461 | } |
| 462 | |
| 463 | #} Interpret results (Result Set - multi-row) |
| 464 | if ( isset( $potentialRes ) && is_array( $potentialRes ) && count( $potentialRes ) > 0 ) { |
| 465 | |
| 466 | #} Has results, tidy + return |
| 467 | foreach ( $potentialRes as $resDataLine ) { |
| 468 | |
| 469 | // tidy (simple) |
| 470 | $resArr = $this->tidy( $resDataLine ); |
| 471 | $res[] = $resArr; |
| 472 | |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | return $res; |
| 477 | } |
| 478 | |
| 479 | // takes $this->objectModel and converts any applicable fields |
| 480 | // into old-form (legacy) $globalFieldArr (as used to be hard-typed in Fields.php Pre DAL3) |
| 481 | #} #FIELDLOADING |
| 482 | public function generateFieldsGlobalArr() { |
| 483 | |
| 484 | if ( isset( $this->objectModel ) && is_array( $this->objectModel ) ) { |
| 485 | |
| 486 | // build it |
| 487 | $retArr = array(); |
| 488 | |
| 489 | // cycle through fields |
| 490 | foreach ( $this->objectModel as $dal3key => $fieldModel ) { |
| 491 | |
| 492 | // if they have 'input_type' then they're designed to be loaded into the global field var |
| 493 | // .. if not, they're DB-only / loaded elsewhere stuff |
| 494 | if ( is_array( $fieldModel ) && isset( $fieldModel['input_type'] ) ) { |
| 495 | |
| 496 | // should be loaded. Build it. |
| 497 | $retArr[ $dal3key ] = array(); |
| 498 | |
| 499 | // Old format as follows: |
| 500 | /* |
| 501 | 'fname' => array( |
| 502 | 'text', // input type |
| 503 | __('First Name',"zero-bs-crm"), // label |
| 504 | 'e.g. John', // placeholder |
| 505 | // extra options: +- |
| 506 | 'options'=>array('Mr', 'Mrs', 'Ms', 'Miss', 'Dr', 'Prof','Mr & Mrs'), |
| 507 | 'essential' => true |
| 508 | 'area'=>__('Main Address',"zero-bs-crm"), |
| 509 | 'migrate'=>'addresses' |
| 510 | 'opt'=>'secondaddress', |
| 511 | ) */ |
| 512 | |
| 513 | // map them in |
| 514 | |
| 515 | // input type = [0] |
| 516 | $retArr[ $dal3key ][0] = $fieldModel['input_type']; |
| 517 | |
| 518 | // input label = [1] |
| 519 | $retArr[ $dal3key ][1] = ''; |
| 520 | if ( isset( $fieldModel['label'] ) ) { |
| 521 | $retArr[ $dal3key ][1] = __( $fieldModel['label'], 'zero-bs-crm' ); |
| 522 | $second_address_label = zeroBSCRM_getSetting( 'secondaddresslabel' ); |
| 523 | if ( empty( $second_address_label ) ) { |
| 524 | $second_address_label = __( 'Second Address', 'zero-bs-crm' ); |
| 525 | } |
| 526 | if ( ! empty( $fieldModel['area'] ) && $fieldModel['area'] == 'Second Address' ) { |
| 527 | $retArr[ $dal3key ][1] .= ' (' . esc_html( $second_address_label ) . ')'; |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | // input placeholder = [2] |
| 532 | $retArr[ $dal3key ][2] = ( isset( $fieldModel['placeholder'] ) ) ? $fieldModel['placeholder'] : ''; |
| 533 | |
| 534 | // extra options (all key-referenced) |
| 535 | // if (isset($fieldModel['options'])) $retArr[$dal3key]['options'] = $fieldModel['options']; |
| 536 | // [options] == [3] in old global obj model world |
| 537 | if ( isset( $fieldModel['options'] ) ) { |
| 538 | $retArr[ $dal3key ][3] = $fieldModel['options']; |
| 539 | } |
| 540 | |
| 541 | if ( isset( $fieldModel['essential'] ) ) { |
| 542 | $retArr[ $dal3key ]['essential'] = $fieldModel['essential']; |
| 543 | } |
| 544 | if ( isset( $fieldModel['area'] ) ) { |
| 545 | $retArr[ $dal3key ]['area'] = $fieldModel['area']; |
| 546 | } |
| 547 | if ( isset( $fieldModel['migrate'] ) ) { |
| 548 | $retArr[ $dal3key ]['migrate'] = $fieldModel['migrate']; |
| 549 | } |
| 550 | if ( isset( $fieldModel['opt'] ) ) { |
| 551 | $retArr[ $dal3key ]['opt'] = $fieldModel['opt']; |
| 552 | } |
| 553 | if ( isset( $fieldModel['nocolumn'] ) ) { |
| 554 | $retArr[ $dal3key ]['nocolumn'] = $fieldModel['nocolumn']; |
| 555 | } |
| 556 | if ( isset( $fieldModel['default'] ) ) { |
| 557 | $retArr[ $dal3key ]['default'] = $fieldModel['default']; |
| 558 | } |
| 559 | |
| 560 | // should all have (where different from dal3key): |
| 561 | if ( isset( $fieldModel['dal1key'] ) ) { |
| 562 | $retArr[ $dal3key ]['dal1key'] = $fieldModel['dal1key']; |
| 563 | } |
| 564 | } |
| 565 | } // / foreach field |
| 566 | |
| 567 | // return built fieldGlobalArr (should mimic old DAL1/2 Fields.php) |
| 568 | return $retArr; |
| 569 | |
| 570 | } |
| 571 | |
| 572 | return array(); |
| 573 | } |
| 574 | |
| 575 | // returns a translation matrix of DAL1key => DAL3key, where possible, using 'dal1key' attribute in data model |
| 576 | public function getDAL1toDAL3ConversionMatrix() { |
| 577 | |
| 578 | $ret = array(); |
| 579 | |
| 580 | if ( isset( $this->objectModel ) && is_array( $this->objectModel ) ) { |
| 581 | |
| 582 | // foreach ($arraySource as $k => $v){ |
| 583 | foreach ( $this->objectModel as $v3Key => $fieldObj ) { |
| 584 | |
| 585 | if ( isset( $fieldObj['dal1key'] ) ) { |
| 586 | |
| 587 | $ret[ $fieldObj['dal1key'] ] = $v3Key; |
| 588 | |
| 589 | } |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | return $ret; |
| 594 | } |
| 595 | |
| 596 | // generic get X (by ID) |
| 597 | // designed to be overriden by each child. |
| 598 | public function getSingle( $ID = -1 ) { |
| 599 | |
| 600 | return false; |
| 601 | } |
| 602 | |
| 603 | /** |
| 604 | * Helper to retrieve Custom Fields with Data for an object |
| 605 | * |
| 606 | * @return array summarised custom fields including values, for object |
| 607 | */ |
| 608 | public function getSingleCustomFields( $ID = -1, $includeHidden = false ) { |
| 609 | |
| 610 | global $zbs; |
| 611 | |
| 612 | if ( $ID > 0 ) { |
| 613 | |
| 614 | // retrieve custom fields |
| 615 | $customFields = $this->getCustomFields( $includeHidden ); |
| 616 | |
| 617 | // retrieve object data |
| 618 | $objectData = $this->getSingle( $ID ); |
| 619 | if ( ! is_array( $objectData ) ) { |
| 620 | $objectData = array(); |
| 621 | } |
| 622 | |
| 623 | // Build return |
| 624 | $return = array(); |
| 625 | if ( is_array( $customFields ) ) { |
| 626 | foreach ( $customFields as $k => $v ) { |
| 627 | |
| 628 | $return[] = array( |
| 629 | 'id' => $v[3], |
| 630 | 'name' => $v[1], |
| 631 | 'value' => ( isset( $objectData[ $v[3] ] ) ? $objectData[ $v[3] ] : '' ), |
| 632 | 'type' => $v[0], |
| 633 | ); |
| 634 | |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | return $return; |
| 639 | |
| 640 | } |
| 641 | |
| 642 | return false; |
| 643 | } |
| 644 | |
| 645 | // generic get X (by IDs) |
| 646 | // designed to be overriden by each child. |
| 647 | public function getIDList( $IDs = array() ) { |
| 648 | |
| 649 | return false; |
| 650 | } |
| 651 | |
| 652 | // generic get X (EVERYTHING) |
| 653 | // designed to be overriden by each child. |
| 654 | // expect heavy load! |
| 655 | public function getAll( $IDs = array() ) { |
| 656 | |
| 657 | return false; |
| 658 | } |
| 659 | |
| 660 | // generic get count of (EVERYTHING) |
| 661 | // designed to be overriden by each child. |
| 662 | public function getFullCount() { |
| 663 | |
| 664 | return false; |
| 665 | } |
| 666 | |
| 667 | // Ownership - simplistic GET owner of obj |
| 668 | public function getOwner( $objID = -1 ) { |
| 669 | |
| 670 | // check |
| 671 | if ( $objID < 1 ) { |
| 672 | return false; |
| 673 | } |
| 674 | |
| 675 | return $this->DAL()->getObjectOwner( |
| 676 | array( |
| 677 | |
| 678 | 'objID' => $objID, |
| 679 | 'objTypeID' => $this->objectType, |
| 680 | |
| 681 | ) |
| 682 | ); |
| 683 | } |
| 684 | |
| 685 | // Ownership - simplistic SET owner of obj |
| 686 | public function setOwner( $objID = -1, $ownerID = -1 ) { |
| 687 | |
| 688 | // check |
| 689 | if ( $objID < 1 || $ownerID < 1 ) { |
| 690 | return false; |
| 691 | } |
| 692 | |
| 693 | // set owner |
| 694 | return $this->DAL()->setObjectOwner( |
| 695 | array( |
| 696 | |
| 697 | 'objID' => $objID, |
| 698 | 'objTypeID' => $this->objectType, |
| 699 | 'ownerID' => $ownerID, |
| 700 | |
| 701 | ) |
| 702 | ); |
| 703 | } |
| 704 | |
| 705 | /** |
| 706 | * Wrapper, use $this->updateMeta($objid,$key,$val) for easy update of obj meta :) |
| 707 | * (Uses built in type) |
| 708 | * |
| 709 | * @param string key |
| 710 | * @param string value |
| 711 | * |
| 712 | * @return bool result |
| 713 | */ |
| 714 | public function updateMeta( $objid = -1, $key = '', $val = '' ) { |
| 715 | |
| 716 | if ( ! empty( $key ) && isset( $this->objectType ) && $this->objectType > 0 ) { // && !empty($val) |
| 717 | |
| 718 | return $this->DAL()->addUpdateMeta( |
| 719 | array( |
| 720 | |
| 721 | 'data' => array( |
| 722 | |
| 723 | 'objid' => $objid, |
| 724 | 'objtype' => $this->objectType, |
| 725 | 'key' => $key, |
| 726 | 'val' => $val, |
| 727 | ), |
| 728 | |
| 729 | ) |
| 730 | ); |
| 731 | |
| 732 | } |
| 733 | |
| 734 | return false; |
| 735 | } |
| 736 | |
| 737 | /** |
| 738 | * tidy's the object from wp db into clean array |
| 739 | * ... also converts uts to local datetime etc. politely |
| 740 | * |
| 741 | * @param array $obj (DB obj) |
| 742 | * |
| 743 | * @return array (clean obj) |
| 744 | */ |
| 745 | public function tidy( $obj = false ) { |
| 746 | |
| 747 | global $zbs; |
| 748 | |
| 749 | $res = false; |
| 750 | |
| 751 | if ( isset( $obj->ID ) ) { |
| 752 | |
| 753 | // THESE must be standard :) |
| 754 | $res = array(); |
| 755 | $res['id'] = (int) $obj->ID; |
| 756 | /* |
| 757 | `zbs_site` INT NULL DEFAULT NULL, |
| 758 | `zbs_team` INT NULL DEFAULT NULL, |
| 759 | `zbs_owner` INT NOT NULL, |
| 760 | */ |
| 761 | $res['owner'] = (int) $obj->zbs_owner; |
| 762 | |
| 763 | // cycle through + pull in |
| 764 | foreach ( $this->objectModel as $dbkey => $val ) { |
| 765 | |
| 766 | // if not already set |
| 767 | if ( ! isset( $res[ $val['fieldname'] ] ) ) { |
| 768 | |
| 769 | switch ( $val['format'] ) { |
| 770 | |
| 771 | case 'int': |
| 772 | $res[ $val['fieldname'] ] = (int) $obj->$dbkey; |
| 773 | break; |
| 774 | |
| 775 | case 'uts': |
| 776 | // normal return |
| 777 | $res[ $val['fieldname'] ] = (int) $obj->$dbkey; |
| 778 | |
| 779 | // auto add locale str |
| 780 | $res[ $val['fieldname'] . '_datestr' ] = zeroBSCRM_locale_utsToDatetime( $obj->$dbkey ); |
| 781 | break; |
| 782 | |
| 783 | default: |
| 784 | $res[ $val['fieldname'] ] = $obj->$dbkey; |
| 785 | break; |
| 786 | |
| 787 | } |
| 788 | } |
| 789 | } |
| 790 | } // if is obj id |
| 791 | |
| 792 | return $res; |
| 793 | } |
| 794 | |
| 795 | /** |
| 796 | * Offers generic custom field tidying, where an obj and it's cleaned version are passed |
| 797 | * ... centralised here as all objects (which have custom fields) had this repeated |
| 798 | * |
| 799 | * @param int $objTypeID e.g. 1 = ZBS_TYPE_CONTACT. |
| 800 | * @param object $obj (DB obj). |
| 801 | * @param array $res (tidied DB obj). |
| 802 | * @param bool $includeAddrCustomFields (whether or not to also probe + tidy custom fields for addrs (mainly contacts + company tidying)). |
| 803 | * |
| 804 | * @return array (clean obj) |
| 805 | */ |
| 806 | public function tidyAddCustomFields( $objTypeID = ZBS_TYPE_CONTACT, $obj = false, $res = false, $includeAddrCustomFields = false ) { |
| 807 | |
| 808 | // vague catch |
| 809 | if ( $obj == false || $res == false ) { |
| 810 | return $res; |
| 811 | } |
| 812 | |
| 813 | #} Retrieve any cf |
| 814 | $customFields = $this->DAL()->getActiveCustomFields( array( 'objtypeid' => $objTypeID ) ); |
| 815 | |
| 816 | if ( is_array( $customFields ) ) { |
| 817 | foreach ( $customFields as $cK => $cF ) { |
| 818 | |
| 819 | // custom field (e.g. 'third name') it'll be passed here as 'third-name' |
| 820 | // ... problem is mysql does not like that :) so we have to chage here: |
| 821 | // in this case we REVERSE this: prepend cf's with cf_ and we switch - for _ |
| 822 | // ... by using $cKey below, instead of cK |
| 823 | $cKey = 'cf_' . str_replace( '-', '_', $cK ); |
| 824 | |
| 825 | $res[ $cK ] = ''; |
| 826 | |
| 827 | // if normal |
| 828 | if ( isset( $obj->$cK ) ) { |
| 829 | $res[ $cK ] = $this->stripSlashes( $obj->$cK ); |
| 830 | } |
| 831 | |
| 832 | // if cf |
| 833 | if ( isset( $obj->$cKey ) ) { |
| 834 | $res[ $cK ] = $this->stripSlashes( $obj->$cKey ); |
| 835 | } |
| 836 | |
| 837 | // if date_type, format |
| 838 | if ( isset( $cF[0] ) && $cF[0] === 'date' ) { |
| 839 | |
| 840 | // make a _date field |
| 841 | if ( '' === $res[ $cK ] ) { |
| 842 | $res[ $cK . '_cfdate' ] = ''; |
| 843 | } else { |
| 844 | $res[ $cK . '_cfdate' ] = zeroBSCRM_date_i18n( -1, $res[ $cK ], false, true ); |
| 845 | $res[ $cK . '_datetime_str' ] = jpcrm_uts_to_datetime_str( $res[ $cK ] ); |
| 846 | $res[ $cK . '_date_str' ] = jpcrm_uts_to_date_str( $res[ $cK ] ); |
| 847 | } |
| 848 | } |
| 849 | } |
| 850 | } |
| 851 | |
| 852 | #} Retrieve addr custfiedls |
| 853 | $addrCustomFields = $this->DAL()->getActiveCustomFields( array( 'objtypeid' => ZBS_TYPE_ADDRESS ) ); |
| 854 | |
| 855 | if ( is_array( $addrCustomFields ) ) { |
| 856 | foreach ( $addrCustomFields as $cK => $cF ) { |
| 857 | |
| 858 | // v2: |
| 859 | // $cKN = (int)$cK+1; |
| 860 | // $cKey = 'addr_cf'.$cKN; |
| 861 | // $cKey2 = 'secaddr_cf'.$cKN; |
| 862 | // v3: |
| 863 | // $cKey = 'addr_'.$cK; |
| 864 | // $cKey2 = 'secaddr_'.$cK; |
| 865 | // v4: |
| 866 | // These keys were causing alias collisions in mysql when keys ended up like 'addr_house-type' |
| 867 | // ... where the sort couldn't be fired for that key due to the - character |
| 868 | // ... so from 4.0.7+ we processed these adding a prefix `addrcf_` (and `secaddrcf_`) and replacing - for _ |
| 869 | $cKey = 'addrcf_' . str_replace( '-', '_', $cK ); |
| 870 | $cKey2 = 'secaddrcf_' . str_replace( '-', '_', $cK ); |
| 871 | |
| 872 | // Note we still want to return as `addr_house-type` not `addrcf_house_type` |
| 873 | $res[ 'addr_' . $cK ] = ''; |
| 874 | $res[ 'secaddr_' . $cK ] = ''; |
| 875 | |
| 876 | // retrieve |
| 877 | if ( isset( $obj->$cKey ) ) { |
| 878 | $res[ 'addr_' . $cK ] = $this->stripSlashes( $obj->$cKey ); |
| 879 | } |
| 880 | if ( isset( $obj->$cKey2 ) ) { |
| 881 | $res[ 'secaddr_' . $cK ] = $this->stripSlashes( $obj->$cKey2 ); |
| 882 | } |
| 883 | |
| 884 | // if date_type, format |
| 885 | if ( isset( $cF[0] ) && $cF[0] == 'date' ) { |
| 886 | |
| 887 | // make a _date field |
| 888 | if ( isset( $res[ 'addr_' . $cK ] ) ) { |
| 889 | $res[ 'addr_' . $cK . '_cfdate' ] = zeroBSCRM_date_i18n( -1, $res[ 'addr_' . $cK ], false, true ); |
| 890 | $res[ 'addr_' . $cK . '_datetime_str' ] = jpcrm_uts_to_datetime_str( $res[ 'addr_' . $cK ] ); |
| 891 | $res[ 'addr_' . $cK . '_date_str' ] = jpcrm_uts_to_date_str( $res[ 'addr_' . $cK ] ); |
| 892 | } |
| 893 | if ( isset( $res[ 'secaddr_' . $cK ] ) ) { |
| 894 | $res[ 'secaddr_' . $cK . '_cfdate' ] = zeroBSCRM_date_i18n( -1, $res[ 'secaddr_' . $cK ], false, true ); |
| 895 | $res[ 'secaddr_' . $cK . '_datetime_str' ] = jpcrm_uts_to_datetime_str( $res[ 'secaddr_' . $cK ] ); |
| 896 | $res[ 'secaddr_' . $cK . '_date_str' ] = jpcrm_uts_to_date_str( $res[ 'secaddr_' . $cK ] ); |
| 897 | } |
| 898 | } |
| 899 | } |
| 900 | } |
| 901 | |
| 902 | return $res; |
| 903 | } |
| 904 | |
| 905 | /** |
| 906 | * this takes a dataArray passed for update/insert and works through |
| 907 | * the fields, checking against the obj model for compliance |
| 908 | * initially this only includes max_len checks as fix for gh-270 |
| 909 | * |
| 910 | * @param array $dataArr (pre-insert obj) |
| 911 | * |
| 912 | * @return bool |
| 913 | */ |
| 914 | public function wpdbChecks( $dataArr = array() ) { |
| 915 | |
| 916 | // req. |
| 917 | global $zbs; |
| 918 | |
| 919 | if ( $this->objectType > 0 && is_array( $dataArr ) && isset( $this->objectDBPrefix ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase,WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase |
| 920 | |
| 921 | // new return |
| 922 | $retArr = $dataArr; |
| 923 | |
| 924 | foreach ( $dataArr as $key => $val ) { |
| 925 | |
| 926 | // use $objectDBPrefix to retrieve the objmodel key |
| 927 | // zbsi_id_override => id_override |
| 928 | $fieldKey = str_replace( $this->objectDBPrefix, '', $key ); |
| 929 | |
| 930 | // max length check? |
| 931 | if ( ! empty( $fieldKey ) && isset( $this->objectModel[ $fieldKey ] ) && isset( $this->objectModel[ $fieldKey ]['max_len'] ) ) { |
| 932 | |
| 933 | // check length |
| 934 | if ( strlen( $val ) > $this->objectModel[ $fieldKey ]['max_len'] ) { |
| 935 | |
| 936 | // > max_len |
| 937 | // .. abbreviate |
| 938 | $retArr[ $key ] = substr( $val, 0, ( $this->objectModel[ $fieldKey ]['max_len'] - 3 ) ) . '...'; |
| 939 | |
| 940 | // Add notice |
| 941 | $label = $fieldKey; |
| 942 | if ( isset( $this->objectModel[ $fieldKey ]['label'] ) ) { |
| 943 | $label = $this->objectModel[ $fieldKey ]['label']; |
| 944 | } |
| 945 | $msg = __( 'The value for the field:', 'zero-bs-crm' ) . ' "' . $label . '" ' . __( 'was too long and has been abbreviated', 'zero-bs-crm' ); |
| 946 | $zbs->DAL->addError( 305, $this->objectType, $msg, $fieldKey ); |
| 947 | |
| 948 | } |
| 949 | } |
| 950 | } |
| 951 | |
| 952 | // return (possibly modified arr) |
| 953 | return $retArr; |
| 954 | |
| 955 | } |
| 956 | |
| 957 | return $dataArr; |
| 958 | } |
| 959 | |
| 960 | /** |
| 961 | * this takes the current database insert/update or any object |
| 962 | * and validates it against the dbmodel for that objtype for uniqueness |
| 963 | * e.g. if a field in the dbmodel has force_unique, it's checked that that field is in fact unique, |
| 964 | * returning false if so |
| 965 | * Note: Blanks side-step this check if attribute 'can_be_blank', but are still are still subject to 'not_empty' check |
| 966 | * ... (verifyNonEmptyValues) if that attribute is specified in the obj model |
| 967 | * |
| 968 | * This'll also add an error to the stack, if it can |
| 969 | * |
| 970 | * @param array $obj (clean obj) |
| 971 | * |
| 972 | * @return bool |
| 973 | */ |
| 974 | public function verifyUniqueValues( $objArr = array(), $id = -1 ) { |
| 975 | |
| 976 | // req. |
| 977 | global $zbs; |
| 978 | |
| 979 | $checksFailed = array(); |
| 980 | |
| 981 | if ( $this->objectType > 0 && is_array( $objArr ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase,WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase |
| 982 | |
| 983 | // DAL3+ we now have proper object models, so can check for 'force_unique' flags against field |
| 984 | |
| 985 | // get an obj model, if set |
| 986 | // note: importantly, v2->v3 migration in v3.0 uses DAL2 objModel drop-in function here, so be aware this may be used during v2->v3 migration |
| 987 | $potentialModel = $zbs->DAL->objModel( $this->objectType ); |
| 988 | |
| 989 | // will be objlayer model if set |
| 990 | if ( is_array( $potentialModel ) ) { |
| 991 | |
| 992 | // cycle through each field verify where necessary |
| 993 | foreach ( $potentialModel as $fieldKey => $fieldDetail ) { |
| 994 | |
| 995 | // there's a few we ignore :) |
| 996 | if ( in_array( $fieldKey, array( 'ID', 'zbs_site', 'zbs_team', 'zbs_owner' ) ) ) { |
| 997 | continue; |
| 998 | } |
| 999 | |
| 1000 | // verify unique fields are unique + unused |
| 1001 | // note. If 'can_be_blank' is also set against the field, blank doesn't get checked here |
| 1002 | if ( isset( $fieldDetail['force_unique'] ) && $fieldDetail['force_unique'] ) { |
| 1003 | |
| 1004 | if ( isset( $fieldDetail['can_be_blank'] ) && $fieldDetail['can_be_blank'] && empty( $objArr[ $fieldKey ] ) ) { |
| 1005 | |
| 1006 | // field is blank, and is allowed to be! |
| 1007 | |
| 1008 | } else { |
| 1009 | |
| 1010 | // needs to ensure field is unique. |
| 1011 | |
| 1012 | // get existing id, if set |
| 1013 | $whereArr = array(); // colname zbsc_email |
| 1014 | $whereArr['uniquecheck'] = array( $fieldDetail['fieldname'], '=', '%s', $objArr[ $fieldKey ] ); |
| 1015 | |
| 1016 | $potentialID = $zbs->DAL->getFieldByWHERE( |
| 1017 | array( |
| 1018 | 'objtype' => $this->objectType, // ZBS_TYPE_CONTACT |
| 1019 | 'colname' => 'ID', |
| 1020 | 'where' => $whereArr, |
| 1021 | 'ignoreowner' => true, |
| 1022 | ) |
| 1023 | ); |
| 1024 | |
| 1025 | // catch dupes (exists, but it's not this) |
| 1026 | if ( $potentialID > 0 && $potentialID != $id ) { |
| 1027 | |
| 1028 | // pass back the failed field. |
| 1029 | $checksFailed[ $fieldKey ] = $fieldDetail; |
| 1030 | |
| 1031 | } |
| 1032 | } |
| 1033 | } |
| 1034 | } // / foreach |
| 1035 | |
| 1036 | } // / if has model |
| 1037 | |
| 1038 | } |
| 1039 | |
| 1040 | // got any fails? |
| 1041 | if ( count( $checksFailed ) > 0 ) { |
| 1042 | |
| 1043 | // can't update, some non-uniques. |
| 1044 | |
| 1045 | // set reason msg |
| 1046 | if ( is_array( $checksFailed ) ) { |
| 1047 | foreach ( $checksFailed as $fieldKey => $fieldDetail ) { |
| 1048 | $fk = $fieldKey; |
| 1049 | if ( isset( $fieldDetail['label'] ) ) { |
| 1050 | $fk = $fieldDetail['label']; |
| 1051 | } |
| 1052 | if ( $fk === 'id_override' ) { |
| 1053 | $msg = __( 'Duplicated reference. The reference should be unique', 'zero-bs-crm' ); |
| 1054 | } else { |
| 1055 | $msg = __( 'The value for the field:', 'zero-bs-crm' ) . ' "' . $fk . '" ' . __( 'was not unique (exists)', 'zero-bs-crm' ); |
| 1056 | } |
| 1057 | |
| 1058 | $zbs->DAL->addError( 301, $this->objectType, $msg, $fieldKey ); |
| 1059 | } |
| 1060 | } |
| 1061 | |
| 1062 | // return fail |
| 1063 | return false; |
| 1064 | |
| 1065 | } // / fails unique field verify |
| 1066 | |
| 1067 | return true; |
| 1068 | } |
| 1069 | |
| 1070 | /** |
| 1071 | * this takes the current database insert/update or any object |
| 1072 | * and validates it against the dbmodel for that objtype for empties |
| 1073 | * e.g. if a field in the dbmodel has not_empty, it's checked that that field is in fact not empty |
| 1074 | * returning false if so |
| 1075 | * Note this is inverse to 'can_be_blank' flag |
| 1076 | * |
| 1077 | * This'll also add an error to the stack, if it can |
| 1078 | * |
| 1079 | * @param array $obj (clean obj) |
| 1080 | * |
| 1081 | * @return bool |
| 1082 | */ |
| 1083 | public function verifyNonEmptyValues( $objArr = array() ) { |
| 1084 | |
| 1085 | // req. |
| 1086 | global $zbs; |
| 1087 | |
| 1088 | $checksFailed = array(); |
| 1089 | |
| 1090 | if ( $this->objectType > 0 && is_array( $objArr ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase,WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase |
| 1091 | |
| 1092 | // DAL3+ we now have proper object models, so can check for 'force_unique' flags against field |
| 1093 | |
| 1094 | // get an obj model, if set |
| 1095 | // note: importantly, v2->v3 migration in v3.0 uses DAL2 objModel drop-in function here, so be aware this may be used during v2->v3 migration |
| 1096 | $potentialModel = $zbs->DAL->objModel( $this->objectType ); |
| 1097 | |
| 1098 | // will be objlayer model if set |
| 1099 | if ( is_array( $potentialModel ) ) { |
| 1100 | |
| 1101 | // cycle through each field verify where necessary |
| 1102 | foreach ( $potentialModel as $fieldKey => $fieldDetail ) { |
| 1103 | |
| 1104 | // verify fields are not empty |
| 1105 | // note. This ignores 'can_be_blank', if is somehow set despite setting not_empty |
| 1106 | if ( isset( $fieldDetail['not_empty'] ) && $fieldDetail['not_empty'] ) { |
| 1107 | |
| 1108 | // needs to ensure field is not empty |
| 1109 | if ( empty( $objArr[ $fieldKey ] ) ) { |
| 1110 | |
| 1111 | // pass back the failed field. |
| 1112 | $checksFailed[ $fieldKey ] = $fieldDetail; |
| 1113 | |
| 1114 | } |
| 1115 | } |
| 1116 | } // / foreach |
| 1117 | |
| 1118 | } // / if has model |
| 1119 | |
| 1120 | } |
| 1121 | |
| 1122 | // got any fails? |
| 1123 | if ( count( $checksFailed ) > 0 ) { |
| 1124 | |
| 1125 | // can't update, some empties. |
| 1126 | |
| 1127 | // set reason msg |
| 1128 | if ( is_array( $checksFailed ) ) { |
| 1129 | foreach ( $checksFailed as $fieldKey => $fieldDetail ) { |
| 1130 | $fk = $fieldKey; |
| 1131 | if ( isset( $fieldDetail['label'] ) ) { |
| 1132 | $fk = $fieldDetail['label']; |
| 1133 | } |
| 1134 | $msg = __( 'The field:', 'zero-bs-crm' ) . ' "' . $fk . '" ' . __( 'is required', 'zero-bs-crm' ); |
| 1135 | $zbs->DAL->addError( 304, $this->objectType, $msg, $fieldKey ); |
| 1136 | } |
| 1137 | } |
| 1138 | |
| 1139 | // return fail |
| 1140 | return false; |
| 1141 | |
| 1142 | } // / fails non blank field verify |
| 1143 | |
| 1144 | return true; |
| 1145 | } |
| 1146 | |
| 1147 | /** |
| 1148 | * remove any non-db fields from the object |
| 1149 | * basically takes array like array('owner'=>1,'fname'=>'x','fullname'=>'x') |
| 1150 | * and returns array like array('owner'=>1,'fname'=>'x') |
| 1151 | * |
| 1152 | * @param array $obj (clean obj) |
| 1153 | * |
| 1154 | * @return array (db ready arr) |
| 1155 | */ |
| 1156 | public function db_ready_obj( $obj = false ) { |
| 1157 | |
| 1158 | global $zbs; |
| 1159 | |
| 1160 | // here it has to use the KEY which is without prefix (e.g. status, not zbsc_status) :) |
| 1161 | if ( isset( $this->objectModel ) && is_array( $this->objectModel ) ) { |
| 1162 | |
| 1163 | $ret = array(); |
| 1164 | if ( is_array( $obj ) ) { |
| 1165 | |
| 1166 | foreach ( $this->objectModel as $fKey => $fObj ) { |
| 1167 | |
| 1168 | if ( isset( $obj[ $fKey ] ) ) { |
| 1169 | $ret[ $fKey ] = $obj[ $fKey ]; |
| 1170 | } |
| 1171 | } |
| 1172 | |
| 1173 | // gross backward compat - long may this die. |
| 1174 | if ( $zbs->db2CompatabilitySupport ) { |
| 1175 | $ret['meta'] = $ret; |
| 1176 | } |
| 1177 | } |
| 1178 | } |
| 1179 | |
| 1180 | return $ret; |
| 1181 | } |
| 1182 | |
| 1183 | /** |
| 1184 | * genericified link array of id's with this obj |
| 1185 | * Takes current area (e.g. EVENT) as first type, and assigns objlinks EVENT -> $toObjType |
| 1186 | * |
| 1187 | * @param int $objectID (int of this obj id) |
| 1188 | * @param array $objectIDsArr (array of ints (IDs)) - NOTE: can pass 'unset' as str to wipe links |
| 1189 | * @param int $toObjectType (int of obj link type) |
| 1190 | * |
| 1191 | * @return bool of action |
| 1192 | */ |
| 1193 | public function addUpdateObjectLinks( $objectID = -1, $objectIDsArr = false, $toObjectType = false ) { |
| 1194 | if ( $toObjectType > 0 && $objectID > 0 ) { |
| 1195 | |
| 1196 | // GENERICIFIED OBJ LINKS |
| 1197 | if ( isset( $objectIDsArr ) && is_array( $objectIDsArr ) && count( $objectIDsArr ) > 0 ) { |
| 1198 | |
| 1199 | // replace existing |
| 1200 | $this->DAL()->addUpdateObjLinks( |
| 1201 | array( |
| 1202 | 'objtypefrom' => $this->objectType, |
| 1203 | 'objtypeto' => $toObjectType, |
| 1204 | 'objfromid' => $objectID, |
| 1205 | 'objtoids' => $objectIDsArr, |
| 1206 | 'mode' => 'replace', |
| 1207 | ) |
| 1208 | ); |
| 1209 | |
| 1210 | return true; |
| 1211 | |
| 1212 | } elseif ( isset( $objectIDsArr ) && $objectIDsArr == 'unset' ) { |
| 1213 | |
| 1214 | // wipe previous links |
| 1215 | $deleted = $this->DAL()->deleteObjLinks( |
| 1216 | array( |
| 1217 | 'objtypefrom' => $this->objectType, |
| 1218 | 'objtypeto' => $toObjectType, |
| 1219 | 'objfromid' => $objectID, |
| 1220 | ) |
| 1221 | ); // where id = |
| 1222 | |
| 1223 | return true; |
| 1224 | |
| 1225 | } |
| 1226 | } |
| 1227 | |
| 1228 | return false; |
| 1229 | } |
| 1230 | |
| 1231 | // =============================================================================== |
| 1232 | // =========== DAL2 WRAPPERS ===================================================== |
| 1233 | // These are dumb, and pass back directly to parent $zbs->DAL equivilents, centralising them |
| 1234 | // ... this is so we can keep $this->lazyTable etc. usage which is simpler than $this->DAL()->lazyTable |
| 1235 | |
| 1236 | public function lazyTable( $objType = -1 ) { |
| 1237 | |
| 1238 | // pass back to main $zbs->DAL |
| 1239 | return $this->DAL()->lazyTable( $objType ); |
| 1240 | } |
| 1241 | public function lazyTidy( $objType = -1, $obj = false ) { |
| 1242 | |
| 1243 | // pass back to main $zbs->DAL |
| 1244 | return $this->DAL()->lazyTidy( $objType, $obj ); |
| 1245 | } |
| 1246 | public function lazyTidyGeneric( $obj = false ) { |
| 1247 | |
| 1248 | // pass back to main $zbs->DAL |
| 1249 | return $this->DAL()->lazyTidyGeneric( $obj ); |
| 1250 | } |
| 1251 | public function space( $str = '', $pre = false ) { |
| 1252 | |
| 1253 | // pass back to main $zbs->DAL |
| 1254 | return $this->DAL()->space( $str, $pre ); |
| 1255 | } |
| 1256 | public function spaceAnd( $str = '' ) { |
| 1257 | |
| 1258 | // pass back to main $zbs->DAL |
| 1259 | return $this->DAL()->spaceAnd( $str ); |
| 1260 | } |
| 1261 | public function spaceWhere( $str = '' ) { |
| 1262 | |
| 1263 | // pass back to main $zbs->DAL |
| 1264 | return $this->DAL()->spaceWhere( $str ); |
| 1265 | } |
| 1266 | public function delimiterIf( $delimiter, $ifStr = '' ) { |
| 1267 | |
| 1268 | // pass back to main $zbs->DAL |
| 1269 | return $this->DAL()->delimiterIf( $delimiter, $ifStr ); |
| 1270 | } |
| 1271 | public function stripSlashes( $obj = false ) { |
| 1272 | return zeroBSCRM_stripSlashes( $obj ); |
| 1273 | } |
| 1274 | public function decodeIfJSON( $str = '' ) { |
| 1275 | |
| 1276 | // pass back to main $zbs->DAL |
| 1277 | return $this->DAL()->decodeIfJSON( $str ); |
| 1278 | } |
| 1279 | public function build_csv( $array = array() ) { |
| 1280 | |
| 1281 | // pass back to main $zbs->DAL |
| 1282 | return $this->DAL()->build_csv( $array ); |
| 1283 | } |
| 1284 | public function buildWhereStr( $whereStr = '', $additionalWhere = '' ) { |
| 1285 | |
| 1286 | // pass back to main $zbs->DAL |
| 1287 | return $this->DAL()->buildWhereStr( $whereStr, $additionalWhere ); |
| 1288 | } |
| 1289 | public function buildWheres( $wheres = array(), $whereStr = '', $params = array(), $andOr = 'AND', $includeInitialWHERE = true ) { |
| 1290 | |
| 1291 | // pass back to main $zbs->DAL |
| 1292 | return $this->DAL()->buildWheres( $wheres, $whereStr, $params, $andOr, $includeInitialWHERE ); |
| 1293 | } |
| 1294 | public function buildSort( $sortByField = '', $sortOrder = 'ASC' ) { |
| 1295 | |
| 1296 | // pass back to main $zbs->DAL |
| 1297 | return $this->DAL()->buildSort( $sortByField, $sortOrder ); |
| 1298 | } |
| 1299 | public function buildPaging( $page = -1, $perPage = -1 ) { |
| 1300 | |
| 1301 | // pass back to main $zbs->DAL |
| 1302 | return $this->DAL()->buildPaging( $page, $perPage ); |
| 1303 | } |
| 1304 | public function buildWPMetaQueryWhere( $metaKey = -1, $metaVal = -1 ) { |
| 1305 | |
| 1306 | // pass back to main $zbs->DAL |
| 1307 | return $this->DAL()->buildWPMetaQueryWhere( $metaKey, $metaVal ); |
| 1308 | } |
| 1309 | public function getTypeStr( $fieldKey = '' ) { |
| 1310 | |
| 1311 | // pass back to main $zbs->DAL |
| 1312 | return $this->DAL()->getTypeStr( $fieldKey ); |
| 1313 | } |
| 1314 | public function prepare( $sql = '', $params = array() ) { |
| 1315 | |
| 1316 | // pass back to main $zbs->DAL |
| 1317 | return $this->DAL()->prepare( $sql, $params ); |
| 1318 | } |
| 1319 | public function catchSQLError( $errObj = -1 ) { |
| 1320 | |
| 1321 | // pass back to main $zbs->DAL |
| 1322 | return $this->DAL()->catchSQLError( $errObj ); |
| 1323 | } |
| 1324 | // legacy signpost, this is now overwritten by DAL->contacts->fullname |
| 1325 | public function format_fullname( $contactArr = array() ) { |
| 1326 | |
| 1327 | // pass back to main $zbs->DAL |
| 1328 | return $this->DAL()->format_fullname( $contactArr ); |
| 1329 | } |
| 1330 | // legacy signpost, this is now overwritten by DAL->[contacts|companies]->format_name_etc |
| 1331 | public function format_name_etc( $contactArr = array(), $args = array() ) { |
| 1332 | |
| 1333 | // pass back to main $zbs->DAL |
| 1334 | return $this->DAL()->format_name_etc( $contactArr, $args ); |
| 1335 | } |
| 1336 | public function format_address( $contactArr = array(), $args = array() ) { |
| 1337 | |
| 1338 | // pass back to main $zbs->DAL |
| 1339 | return $this->DAL()->format_address( $contactArr, $args ); |
| 1340 | } |
| 1341 | public function makeSlug( $string, $replace = array(), $delimiter = '-' ) { |
| 1342 | |
| 1343 | // pass back to main $zbs->DAL |
| 1344 | return $this->DAL()->makeSlug( $string, $replace, $delimiter ); |
| 1345 | } |
| 1346 | public function makeSlugCleanStr( $string = '', $delimiter = '-' ) { |
| 1347 | |
| 1348 | // pass back to main $zbs->DAL |
| 1349 | return $this->DAL()->makeSlugCleanStr( $string, $delimiter ); |
| 1350 | } |
| 1351 | public function ownershipQueryVars( $ignoreOwner = false ) { |
| 1352 | |
| 1353 | // pass back to main $zbs->DAL |
| 1354 | return $this->DAL()->ownershipQueryVars( $ignoreOwner ); |
| 1355 | } |
| 1356 | public function ownershipSQL( $ignoreOwner = false, $table = '' ) { |
| 1357 | |
| 1358 | // pass back to main $zbs->DAL |
| 1359 | return $this->DAL()->ownershipSQL( $ignoreOwner, $table ); |
| 1360 | } |
| 1361 | public function addUpdateCustomField( $args = array() ) { |
| 1362 | |
| 1363 | // pass back to main $zbs->DAL |
| 1364 | return $this->DAL()->addUpdateCustomField( $args ); |
| 1365 | } |
| 1366 | |
| 1367 | /** |
| 1368 | * Fixes name clashes between linked objects (e.g. company) and custom fields with the same slug. |
| 1369 | * Adds a suffix to conflicting field names to prevent clashes. |
| 1370 | * |
| 1371 | * @param array &$array The array containing the fields that need to be checked for name clashes. |
| 1372 | * @param string[] $keys An array of keys to check within the provided array for potential name clashes. |
| 1373 | * |
| 1374 | * @return void |
| 1375 | * |
| 1376 | * @see https://github.com/Automattic/zero-bs-crm/issues/3477 |
| 1377 | */ |
| 1378 | public function add_name_clash_suffix_if_needed( &$array, $keys ) { |
| 1379 | foreach ( $keys as $key ) { |
| 1380 | if ( isset( $array[ $key ] ) ) { |
| 1381 | $this->name_clashes_temp_fix[] = $key; |
| 1382 | $new_key = $key . self::NAME_CLASH_FIX_SUFFIX; |
| 1383 | $array[ $new_key ] = $array[ $key ]; |
| 1384 | } |
| 1385 | } |
| 1386 | } |
| 1387 | |
| 1388 | /** |
| 1389 | * Fixes a field name if it has been identified as having a name clash by appending a suffix. |
| 1390 | * |
| 1391 | * @param string &$field_name The field name to check and potentially modify to avoid a name clash. |
| 1392 | * |
| 1393 | * @return void |
| 1394 | * |
| 1395 | * @see https://github.com/Automattic/zero-bs-crm/issues/3477 |
| 1396 | */ |
| 1397 | public function fix_name_clash_if_needed( &$field_name ) { |
| 1398 | if ( in_array( $field_name, $this->name_clashes_temp_fix, true ) ) { |
| 1399 | $field_name = $field_name . self::NAME_CLASH_FIX_SUFFIX; |
| 1400 | } |
| 1401 | } |
| 1402 | |
| 1403 | // =========== / DAL2 WRAPPERS =================================================== |
| 1404 | // =============================================================================== |
| 1405 | } // / class |