PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/types/application/controllers/ajax/handler/field_control_action.php

https://gitlab.com/Fraternal-Group/fraternal
PHP | 364 lines | 201 code | 60 blank | 103 comment | 59 complexity | 515821aea03f84deff32fc8863d03148 MD5 | raw file
  1. <?php
  2. /**
  3. * Handle action with field definitions on the Field Control page.
  4. *
  5. * @since 2.1
  6. */
  7. final class Types_Ajax_Handler_Field_Control_Action extends Types_Ajax_Handler_Abstract {
  8. /**
  9. * @inheritdoc
  10. *
  11. * todo document
  12. *
  13. * @param array $arguments
  14. */
  15. public function process_call( $arguments ) {
  16. $am = $this->get_am();
  17. $am->ajax_begin( array( 'nonce' => $am->get_action_js_name( Types_Ajax::CALLBACK_FIELD_CONTROL_ACTION ) ) );
  18. // Read and validate input
  19. $field_action = wpcf_getpost( 'field_action' );
  20. $fields = wpcf_getpost( 'fields' );
  21. $current_domain = wpcf_getpost( 'domain', null, Types_Field_Utils::get_domains() );
  22. if( null == $current_domain ) {
  23. $am->ajax_finish( array( 'message' => __( 'Wrong field domain.', 'wpcf' ) ), false );
  24. }
  25. if( !is_array( $fields ) || empty( $fields ) ) {
  26. $am->ajax_finish( array( 'message' => __( 'No fields have been selected.', 'wpcf' ) ), false );
  27. }
  28. $action_specific_data = wpcf_getpost( 'action_specific', array() );
  29. // Process fields one by one
  30. $errors = array();
  31. $results = array();
  32. foreach( $fields as $field ) {
  33. $result = $this->single_field_control_action( $field_action, $field, $current_domain, $action_specific_data );
  34. if( is_array( $result ) ) {
  35. // Array of errors
  36. $errors = array_merge( $errors, $result );
  37. } else if( $result instanceof WP_Error ) {
  38. // Single error
  39. $errors[] = $result;
  40. } else if( false == $result ) {
  41. // This should not happen...!
  42. $errors[] = new WP_Error( 0, __( 'An unexpected error happened while processing the request.', 'wpcf' ) );
  43. } else {
  44. // Success
  45. // Save the field definition model as a result if we got a whole definition
  46. if( $result instanceof WPCF_Field_Definition ) {
  47. $result = $result->to_json();
  48. }
  49. $results[ wpcf_getarr( $field, 'slug' ) ] = $result;
  50. }
  51. }
  52. $data = array( 'results' => $results );
  53. $is_success = empty( $errors );
  54. if( !$is_success ) {
  55. $error_messages = array();
  56. /** @var WP_Error $error */
  57. foreach( $errors as $error ) {
  58. $error_messages[] = $error->get_error_message();
  59. }
  60. $data['messages'] = $error_messages;
  61. }
  62. $am->ajax_finish( $data, $is_success );
  63. }
  64. /**
  65. * @param string $action_name One of the allowed action names: 'manage_with_types'
  66. * @param array $field Field definition model passed from JS.
  67. * @param string $domain Field domain name.
  68. * @param mixed $action_specific_data
  69. * @return bool|mixed|null|WP_Error|WP_Error[]|WPCF_Field_Definition An error, array of errors, boolean indicating
  70. * success or a result value to be passed back to JS.
  71. * @since 2.0
  72. */
  73. private function single_field_control_action( $action_name, $field, $domain, $action_specific_data ) {
  74. $field_slug = wpcf_getarr( $field, 'slug' );
  75. switch ( $action_name ) {
  76. case 'manage_with_types':
  77. return $this->start_managing_field( wpcf_getarr( $field, 'metaKey' ), $domain );
  78. case 'stop_managing_with_types':
  79. return $this->stop_managing_field( $field_slug, $domain );
  80. case 'change_group_assignment':
  81. return $this->change_assignment_to_groups( $field_slug, $domain, $action_specific_data );
  82. case 'delete_field':
  83. return $this->delete_field( $field_slug, $domain );
  84. case 'change_field_type':
  85. return $this->change_field_type( $field_slug, $domain, $action_specific_data );
  86. case 'change_field_cardinality':
  87. return $this->change_field_cardinality( $field_slug, $domain, $action_specific_data );
  88. default:
  89. return new WP_Error( 42, __( 'Invalid action name.', 'wpcf' ) );
  90. }
  91. }
  92. /**
  93. * Start managing a field with given meta_key with Types.
  94. *
  95. * Looks if there already exists a field definition that uses the meta_key. If yes, it's most probably a "disabled"
  96. * one, that is stored only for the possibility of later "re-activation" (which is happening now). In that case,
  97. * the field definition will be simply updated.
  98. *
  99. * If there is no matching field definition whatsoever, it will be created with in some default manner.
  100. * Check WPCF_Field_Definition_Factory::create_field_definition_for_existing_fields() for details.
  101. *
  102. * AJAX callback helper only, do not use elsewhere.
  103. *
  104. * @param string $meta_key
  105. * @param string $domain Field domain
  106. * @return false|null|WPCF_Field_Definition The updated/newly created field definition or falsy value on failure.
  107. * @since 2.0
  108. */
  109. public function start_managing_field( $meta_key, $domain ) {
  110. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  111. $definition = $factory->meta_key_belongs_to_types_field( $meta_key, 'definition' );
  112. if( null == $definition ) {
  113. $result = $factory->create_field_definition_for_existing_fields( $meta_key );
  114. if( false != $result ) {
  115. return $factory->load_field_definition( $result );
  116. } else {
  117. return false;
  118. }
  119. } else {
  120. $is_success = $definition->set_types_management_status( true );
  121. return ( $is_success ? $definition : false );
  122. }
  123. }
  124. /**
  125. * Stop managing a field with given field slug by Types.
  126. *
  127. * AJAX callback helper only, do not use elsewhere.
  128. *
  129. * @param string $field_slug
  130. * @param string $domain Field domain.
  131. * @return WP_Error|WPCF_Field_Definition Error with a user-friendly message on failure
  132. * or the updated definition on success.
  133. * @since 2.0
  134. */
  135. public static function stop_managing_field( $field_slug, $domain ) {
  136. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  137. $definition = $factory->load_field_definition( $field_slug );
  138. if( null == $definition ) {
  139. return new WP_Error( 42, sprintf( __( 'Field definition for field "%s" not found in options.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  140. } else {
  141. $is_success = $definition->set_types_management_status( false );
  142. if( $is_success ) {
  143. return $definition;
  144. } else {
  145. return new WP_Error(
  146. 42,
  147. sprintf(
  148. __( 'Unable to set types management status for field definition "%s".', 'wpcf' ),
  149. sanitize_text_field( $field_slug )
  150. )
  151. );
  152. }
  153. }
  154. }
  155. /**
  156. * Change which groups is a field definition associated with.
  157. *
  158. * AJAX callback helper only, do not use elsewhere.
  159. *
  160. * @param string $field_slug Field definition slug.
  161. * @param string $domain Field domain
  162. * @param string[][] $groups Action-specific data passed through AJAX. Array containing a single key 'group_slugs',
  163. * containing an array of field group slugs.
  164. *
  165. * @return WP_Error|WPCF_Field_Definition The updated field definition on success or an error object.
  166. * @since 2.0
  167. */
  168. public function change_assignment_to_groups( $field_slug, $domain, $groups ) {
  169. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  170. $definition = $factory->load_field_definition( $field_slug );
  171. if( null == $definition ) {
  172. return new WP_Error( 42, sprintf( __( 'Field definition for field "%s" not found in options.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  173. }
  174. $new_groups = wpcf_ensarr( wpcf_getarr( $groups, 'group_slugs' ) );
  175. $associated_groups = $definition->get_associated_groups();
  176. $is_success = true;
  177. foreach( $associated_groups as $group ) {
  178. if( !in_array( $group->get_slug(), $new_groups ) ) {
  179. $is_success = $is_success && $group->remove_field_definition( $definition );
  180. }
  181. }
  182. $group_factory = $factory->get_group_factory();
  183. foreach( $new_groups as $new_group_slug ) {
  184. $new_group = $group_factory->load_field_group( $new_group_slug );
  185. if( null != $new_group ) {
  186. $is_success = $is_success && $new_group->add_field_definition( $definition );
  187. } else {
  188. $is_success = false;
  189. }
  190. }
  191. if( $is_success ) {
  192. return $definition;
  193. } else {
  194. return new WP_Error();
  195. }
  196. }
  197. /**
  198. * Delete a field definition and all values of the field within given domain.
  199. *
  200. * @param string $field_slug
  201. * @param string $domain
  202. * @return bool|WP_Error True for success, false or WP_Error on error.
  203. * @since 2.0
  204. */
  205. public function delete_field( $field_slug, $domain ) {
  206. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  207. $definition = $factory->load_field_definition( $field_slug );
  208. if( null == $definition ) {
  209. return new WP_Error( 42, sprintf( __( 'Field definition for field "%s" not found in options.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  210. } else if( ! $definition->is_managed_by_types() ) {
  211. return new WP_Error( 42, sprintf( __( 'Field "%s" will not be deleted because it is not managed by Types.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  212. }
  213. $response = $factory->delete_definition( $definition );
  214. return $response;
  215. }
  216. /**
  217. * Change a field type for given field definition.
  218. *
  219. * Performs checks if the conversion is allowed, and if not, generate a proper error message.
  220. *
  221. * @param string $field_slug
  222. * @param string $domain
  223. * @param string[] $arguments Needs to contain the 'field_type' key with target type slug.
  224. * @return false|WP_Error|WPCF_Field_Definition The updated definition on succes, error/false otherwise.
  225. * @since 2.0
  226. */
  227. public function change_field_type( $field_slug, $domain, $arguments ) {
  228. // Load all information we need, fail if it doesn't exist.
  229. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  230. $definition = $factory->load_field_definition( $field_slug );
  231. if( null == $definition ) {
  232. return new WP_Error( 42, sprintf( __( 'Field definition for field "%s" not found in options.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  233. } else if( ! $definition->is_managed_by_types() ) {
  234. return new WP_Error( 42, sprintf( __( 'Field "%s" will not be converted because it is not managed by Types.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  235. }
  236. $type_slug = wpcf_getarr( $arguments, 'field_type' );
  237. $target_type = Types_Field_Type_Definition_Factory::get_instance()->load_field_type_definition( $type_slug );
  238. if( null == $target_type ) {
  239. return new WP_Error( 42, sprintf( __( 'Unknown field type "%s".', 'wpcf' ), $type_slug ) );
  240. }
  241. // Check if we can convert between types
  242. $is_conversion_possible = Types_Field_Type_Converter::get_instance()->is_conversion_possible( $definition->get_type(), $target_type );
  243. if( !$is_conversion_possible ) {
  244. return new WP_Error(
  245. 42,
  246. sprintf(
  247. __( 'Conversion from type "%s" to "%s" is currently not allowed.', 'wpcf' ),
  248. $definition->get_type()->get_display_name(),
  249. $target_type->get_display_name()
  250. )
  251. );
  252. }
  253. // Check if we can do the conversion with current field's cardinality
  254. $is_cardinality_sustainable = ( ! $definition->get_is_repetitive() || $target_type->can_be_repetitive() );
  255. if( !$is_cardinality_sustainable ) {
  256. return new WP_Error(
  257. 42,
  258. sprintf(
  259. __( 'Field "%s" cannot be converted from "%s" to "%s" because it is repetitive and the target type doesn\'t support that.', 'wpcf' ),
  260. $definition->get_display_name(),
  261. $definition->get_type()->get_display_name(),
  262. $target_type->get_display_name()
  263. )
  264. );
  265. }
  266. // All is fine, proceed.
  267. $result = $definition->change_type( $target_type );
  268. if( $result ) {
  269. return $definition;
  270. } else {
  271. // Something unexpected went wrong.
  272. return false;
  273. }
  274. }
  275. /**
  276. * Change cardinality of given field, if it is permitted by its type.
  277. *
  278. * @param string $field_slug Field definition slug.
  279. * @param string $domain Field domain.
  280. * @param string[] $arguments Needs to contain the 'target_cardinality' key with 'single'|'repetitive' value.
  281. * @return bool|WP_Error|WPCF_Field_Definition The updated definition on succes, error/false otherwise.
  282. * @since 2.0
  283. */
  284. public function change_field_cardinality( $field_slug, $domain, $arguments ) {
  285. $factory = WPCF_Field_Definition_Factory::get_factory_by_domain( $domain );
  286. $definition = $factory->load_field_definition( $field_slug );
  287. if( null == $definition ) {
  288. return new WP_Error( 42, sprintf( __( 'Field definition for field "%s" not found in options.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  289. } else if( ! $definition->is_managed_by_types() ) {
  290. return new WP_Error( 42, sprintf( __( 'Field "%s" will not be converted because it is not managed by Types.', 'wpcf' ), sanitize_text_field( $field_slug ) ) );
  291. }
  292. $target_cardinality = wpcf_getarr( $arguments, 'target_cardinality', null, array( 'single', 'repetitive' ) );
  293. if( null == $target_cardinality ) {
  294. return false;
  295. }
  296. $set_as_repetitive = ( 'repetitive' == $target_cardinality );
  297. $result = $definition->set_is_repetitive( $set_as_repetitive );
  298. if( $result ) {
  299. return $definition;
  300. } else {
  301. return false;
  302. }
  303. }
  304. }