I know I posted an answer to this already, but I've found a better solution, so I wanted to take the time to share that.
As it turns out, there is one small hiccup with the JS solution in my previous answer that didn't surface until I had a friend test my in progress application.
The Problem
Previously, I defined Job Steps as a Repeater Field with two sub-fields: Title and Details. The Details sub-field is a WYSIWYG field to give users some control over formatting.
The problem is, tinyMCE uses JavaScript that wraps plain textarea
inputs in the user-friendly WYSIWYG textarea
after a page has loaded. This caused problems, because my JS solution:
function my_loadJobArchetype() {
$('<div>').load("mysite.com/edit-archetype/?archetypeId=123"
+ ' ' + "[data-name='my_job_steps']", function() {
$("[data-name='my_job_steps']").replaceWith(
$(this).children("[data-name='my_job_steps']")
);
});
}
Didn't execute scripts during the load()
call. When I replaced the blank Job content with the content loaded from the archetype, my users were getting a plain text box rather than a WYSIWYG editor.
So, instead of seeing things like Quantity, they saw:
<p><strong>Quantity</strong></p>
Needless to say, that's not what I wanted to happen.
I followed jgraup's suggestion and looked into some of the ACF plugins, but it turns out I can solve it with the filters already provided by the plugin.
The filter we want to use for this is: acf/load_value/key=[field_key]
. Basically, this is a dynamic filter that lets you hook into the LOADING process for specific custom fields.
Step 1
Redefine my custom fields (slightly).
Originally, Jobs and Job Archetypes used the same field group: wf_jobStepFields
, but for this process to work correctly, I need to maintain two copies of the fields:
- wf_jobStepFields
- wf_archetypeStepFields
Within each of those field groups is a unique repeater field: wf_job_steps
and wf_archetype_steps
. The sub-field keys remain the same (step_title
and step_details
)
Step 2
Hook into the ACF filter
function my_loadArchetypeValue($value, $post_id, $field) {
}
add_filter('acf/load_value/key=wf_job_steps', 'my_loadArchetypeValue', 10, 3);
"acf/load_value/key={$field_key}"
is a flexible filter hook that allows us to latch onto the loading process for an individual field. In this case, we're latching on to the wf_job_steps
Repeater Field
Step 3
Override values on the New Job Form
I've got a page on the front-end that users can visit to submit new Jobs. By default, this page contains a blank job with a single Job Step defined, but using this hook, we can override that with the selected Archetype template:
function my_loadArchetypeValue($value, $post_id, $field) {
// FIRST: Check to make sure we are on the NEW JOB page.
if (is_page('new-job')) {
// Check to see if the "archetypeId" query variable is defined.
if (get_query_var("archetypeId")) {
// If it is, grab the VALUE of the ARCHETYPE STEPS for the selected Archetype
return get_field('wf_archetype_steps',get_query_var("archetypeId"));
}
}
}
Step 4
Override values on the Admin screen
Although I have a front-end form for adding new Jobs, I want to make sure that the backend screen provides the same functionality so that Archetypes can be leveraged from the admin area as well.
// First, check to make sure that the get_current_screen() function is defined.
// It is not defined on every admin page, but it is defined on the
// ADD NEW pages, which is where we want to be.
if (function_exists("get_current_screen")) {
// Grab the current screen OBJECT and save it
$screen = get_current_screen();
// This statement checks to make sure that we are on the
// ADD NEW screen for the JOB post type
if ($screen->action == 'add' && $screen->post_type == wf-job) {
$archetypeId = $_GET["archetypeId"];
/* NOTE: the global WP_Query object has no query vars here,
/ so we need to get a little "creative to pull the
/ variable from the URL.
Technically, $_GET["archetypeId"] will also work on the
Add Job form as well, but we have access to the WP_Query
query vars there, and I think that using the WP core whenever
possible makes WordPress related functions easier to follow
*/
if ($archetypeId) {
return get_field('wf_archetype_steps', $archetypeId);
}
}
}
The Final Result
Put it all together, and the final function looks like this:
function my_loadArchetypeValue($value, $post_id, $field) {
if (is_page('new-job')) {
$archetypeId = get_query_var("archetypeId");
if ($archetypeId) {
return get_field('wf_archetype_steps',$archetypeId);
}
} elseif (function_exists("get_current_screen")) {
$screen = get_current_screen();
if ($screen->action == 'add' && $screen->post_type == wf-job) {
$archetypeId = $_GET["archetypeId"];
if ($archetypeId) {
return get_field('wf_archetype_steps', $archetypeId);
}
}
}
return $value;
}
add_filter('acf/load_value/key=wf_job_steps', 'my_loadArchetypeValue', 10, 3);
NOTE: As a rule, I'm not a huge fan of defining a second field group. In this case, it's pretty much a textbook definition of copy/pasting code, since the field groups are functionally identical aside from a couple keys.
But, there is a very good reason for doing it in this case.
The filter we hook into latches on to the LOAD event for a specific field (defined by the field's KEY). In this case, we're latching onto wf_job_steps.
If I had continued to use a single field group, this filter hook would throw the process into an infinite loop, because each time my filter function hit the get_field()
call, the filter would be triggered again.
Defining a second field group with a unique KEY for the repeater field prevents the infinite loop, but since the sub-fields still have the same KEY values, it allows them to transfer seamlessly over to new JOBS.