Skip to content

Commit

Permalink
Merge pull request #913 from Codeinwp/fix/title-with-chain-actions
Browse files Browse the repository at this point in the history
Allow chained actions for `[#item_title]`
  • Loading branch information
vytisbulkevicius authored May 30, 2024
2 parents 510d320 + b987a2b commit 3a06eb9
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 80 deletions.
3 changes: 3 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,7 @@ module.exports = defineConfig({
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
pageLoadTimeout : 300000,
},
blockHosts: [
'app.formbricks.com'
],
})
80 changes: 70 additions & 10 deletions cypress/e2e/default/import_feed_free.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,72 @@ describe('Test Free - Import Feed', function() {
cy.get('.fz-tabs-menu li a').should('have.length', settings.tabs);
})

it('Create/Verify/Run import', function() {
it('Check chained actions insertion for title', function() {
// Create a new import.
cy.visit('/wp-admin/post-new.php?post_type=feedzy_imports');

// Fill up the form
cy.get('#post_title').clear().type( 'Chained actions: ' + feed.url );
cy.get('#feedzy-import-source').clear().type( feed.url );
cy.get('#feedzy-import-source').next('.fz-input-group-append').find('.add-outside-tags').click();

// Open the Map Content tab
cy.get('#fz-import-map-content > .feedzy-accordion-item__button').click();

// Clear the title field.
cy.get(':nth-child(4) > .fz-right > .fz-form-group > .fz-input-group > .fz-input-group-left > .fz-group > .tagify > .tagify__input').type('{selectall}{backspace}');

// Add chained actions for title field.
cy.get(':nth-child(4) > .fz-right > .fz-form-group > .fz-input-group > .fz-input-group-right > .dropdown > .btn').click();
cy.get(':nth-child(4) > .fz-right > .fz-form-group > .fz-input-group > .fz-input-group-right > .dropdown > .dropdown-menu > [data-field-tag="item_title"] > small').click(); // Select the [#item_title] tag.

// Add a chained action.
cy.get('.fz-action-relative > .components-button').click();
cy.get('.popover-action-list > ul > :nth-child(1)').click(); // Select the "Trim content" action.

// Add another chained action.
cy.get('.fz-action-relative > .components-button').click();
cy.get('.popover-action-list > ul > :nth-child(3)').click(); // Select the "Search and Replace" action.

// Save actions.
cy.get('.fz-save-action').click();

// Save the serialized data directly to the input field. (The Tagify lib used for UI does not update the input field based on Cypress click actions).
cy.get('.tagify__input > .tagify__tag a').invoke('attr', 'data-actions').then((serializedActions) => {
cy.get('[name="feedzy_meta_data[import_post_title]"]').clear({force: true}).invoke('val', '[[{"value": "' + serializedActions + '"}]]' ).blur({force: true});
});

// Save the import.
cy.get('button[type="submit"][name="save"]').scrollIntoView().click({force:true});

// Reopen the import.
cy.visit('/wp-admin/edit.php?post_type=feedzy_imports');
cy.get('tr:nth-of-type(1) .row-title').click();

// Open the Map Content tab
cy.get('#fz-import-map-content > .feedzy-accordion-item__button').click();

// Check the saved actions.
cy.get('.tagify__input > .tagify__tag').should('be.visible');
cy.get('.tagify__input > .tagify__tag a').invoke('attr', 'data-actions').then((serializedActions) => {
expect(serializedActions).to.include('item_title');
expect(serializedActions).to.include('trim');
expect(serializedActions).to.include('search_replace');

// Check if we have right actions.
const actions = JSON.parse(decodeURIComponent(serializedActions));

expect(actions).to.have.length(2);
expect(actions[0].id).to.equal('trim');
expect(actions[0].tag).to.equal('item_title');

expect(actions[1].id).to.equal('search_replace');
expect(actions[1].tag).to.equal('item_title');
} );

})

it('Check the Create/Verify/Run workflow for feed import', function() {
// 1. CREATE
cy.visit('/wp-admin/post-new.php?post_type=feedzy_imports');

Expand Down Expand Up @@ -84,18 +149,15 @@ describe('Test Free - Import Feed', function() {

// show a notice.
//cy.get('div.notice.feedzy-error-critical').should('be.visible');
})

it('Update the new import with VALID url', function() {

cy.visit('/wp-admin/edit.php?post_type=feedzy_imports');

cy.get('tr:nth-of-type(1) .row-title').click();


// fill up the form
cy.get('#post_title').clear().type( feed.url );
cy.get('#feedzy-import-source').clear().type( feed.url );
cy.get('#feedzy-import-source').next('.fz-input-group-append').find('.add-outside-tags').click();


cy.get('button[type="submit"][name="save"]').scrollIntoView().click({force:true});

// check if the import has been setup
Expand All @@ -117,7 +179,7 @@ describe('Test Free - Import Feed', function() {
cy.get('#feedzy_post_terms').invoke('show').then( () => {
cy.get('#feedzy_post_terms option:selected').should('have.length', feed.taxonomy.length);
});

cy.get('[name="feedzy_meta_data[import_post_title]"]').should('have.value', PREFIX + feed.title);
cy.get('[name="feedzy_meta_data[import_post_content]"]').should('have.value', PREFIX + feed.fullcontent.content + feed.content + '\n');

Expand Down Expand Up @@ -254,6 +316,4 @@ describe('Test Free - Import Feed', function() {
cy.get('.wp-block-post-author-name .wp-block-post-author-name__link:contains("wordpress")').should('exist');

})


})
88 changes: 54 additions & 34 deletions includes/admin/feedzy-rss-feeds-actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ class Feedzy_Rss_Feeds_Actions {
private static $instance;

/**
* Content actions.
* Serialized content actions. It can contain a mix of magic tags and simple text.
*
* @var string $actions Content actions.
* @var string $raw_serialized_actions Content actions.
*/
private $actions;
private $raw_serialized_actions;

/**
* Setting options.
Expand All @@ -39,7 +39,7 @@ class Feedzy_Rss_Feeds_Actions {
/**
* Extract tags.
*
* @var string $extract_tags Extract tags.
* @var array $extract_tags Extract tags.
*/
private $extract_tags;

Expand Down Expand Up @@ -72,11 +72,11 @@ class Feedzy_Rss_Feeds_Actions {
public $result = '';

/**
* Post content.
* The field content (title, description, post content, date, etc.)
*
* @var string $post_content
* @var string $field_content
*/
public $post_content = '';
public $field_content = '';

/**
* Default value.
Expand Down Expand Up @@ -129,13 +129,13 @@ public static function instance() {
/**
* Run actions.
*
* @param string $actions Item content actions.
* @return string
* @param string $raw_serialized_actions Item content actions.
* @return string|array
*/
public function set_actions( $actions = '' ) {
$this->actions = $actions;
if ( empty( $this->actions ) ) {
return $this->actions;
public function set_raw_serialized_actions( $raw_serialized_actions = '' ) {
$this->raw_serialized_actions = $raw_serialized_actions;
if ( empty( $this->raw_serialized_actions ) ) {
return $this->raw_serialized_actions;
}
$this->extract_tags = $this->extract_magic_tags();
return $this->extract_tags;
Expand All @@ -154,10 +154,23 @@ public function set_settings( $options ) {
/**
* Extract magic tags.
*
* @return string
* @return array|array[]
*/
public function extract_magic_tags() {
preg_match_all( '/\[\[\{(.*)\}\]\]/U', $this->actions, $item_magic_tags, PREG_PATTERN_ORDER );
/**
* Transform the serialized string of magic tags to array.
*
* Input(string): [[{"value":"[{"id":"chat_gpt_rewrite","tag":"item_title","data":{"ChatGPT":"Create a long description: {content}"}},{"id":"fz_summarize","tag":"item_title","data":{"fz_summarize":true}}]"}]] with a nice weather.
*
* Output:
* [
* [
* [replace_to] => [[{"value":"[{"id":"chat_gpt_rewrite","tag":"item_title","data":{"ChatGPT":"Create a long description: {content}"}},{"id":"fz_summarize","tag":"item_title","data":{"fz_summarize":true}}]"}]]
* [replace_with] => [{"id":"chat_gpt_rewrite","tag":"item_title","data":{"ChatGPT":"Create a long description: {content}"}},{"id":"fz_summarize","tag":"item_title","data":{"fz_summarize":true}}]
* ]
* ]
*/
preg_match_all( '/\[\[\{(.*)\}\]\]/U', $this->raw_serialized_actions, $item_magic_tags, PREG_PATTERN_ORDER );
$extract_tags = array();
if ( ! empty( $item_magic_tags[0] ) ) {
$extract_tags = array_map(
Expand All @@ -175,12 +188,14 @@ function( $tag ) {
}

/**
* Get magic tags.
* Get the extracted serialized actions from the Tagify tags. The actions can be a mix of Tagify tags and simple text.
*
* @return string The serialized actions.
*/
public function get_tags() {
public function get_serialized_actions() {
$replace_to = array_column( $this->get_extract_tags(), 'replace_to' );
$replace_with = array_column( $this->get_extract_tags(), 'replace_with' );
return str_replace( $replace_to, $replace_with, $this->actions );
return str_replace( $replace_to, $replace_with, $this->raw_serialized_actions );
}

/**
Expand All @@ -191,17 +206,21 @@ public function get_extract_tags() {
}

/**
* Get actions.
* Get actions. Return pairs of serialized actions and their deserialized versions.
*
* Deserialized version is used to run the action job. While serialized version is used to replace the job result in the input content.
*
* @return array
*/
public function get_actions() {
$replace_with = array_column( $this->get_extract_tags(), 'replace_with' );
$actions = array_map(
function( $action ) {
$replace_with = json_decode( $action );
if ( $replace_with ) {
function( $serialized_actions ) {
$job_actions = json_decode( $serialized_actions );
if ( $job_actions ) {
return array(
'replace_to' => wp_json_encode( $replace_with ),
'replace_with' => $replace_with,
'serialized_actions' => $serialized_actions,
'job_actions' => $job_actions,
);
}
return false;
Expand All @@ -214,46 +233,47 @@ function( $action ) {
/**
* Run action job.
*
* @param string $post_content Post content.
* @param string $field_content Field content. It can contain a mix of magic tags and simple text.
* @param string $import_translation_lang Translation language code.
* @param object $job Post object.
* @param string $language_code Feed language code.
* @param array $item Feed item.
* @param string $default_value Default value.
* @return string
*/
public function run_action_job( $post_content, $import_translation_lang, $job, $language_code, $item, $default_value = '' ) {
public function run_action_job( $field_content, $import_translation_lang, $job, $language_code, $item, $default_value = '' ) {
$this->item = $item;
$this->job = $job;
$this->language_code = $language_code;
$this->translation_lang = $import_translation_lang;
$this->post_content = $post_content;
$this->field_content = $field_content;
$this->default_value = $default_value;
$actions = $this->get_actions();

if ( ! empty( $actions ) ) {
foreach ( $actions as $key => $jobs ) {
if ( ! isset( $jobs['replace_with'] ) ) {
if ( ! isset( $jobs['job_actions'] ) ) {
continue;
}

$this->result = null;
$replace_with = isset( $jobs['replace_with'] ) ? $jobs['replace_with'] : array();
$replace_to = isset( $jobs['replace_to'] ) ? $jobs['replace_to'] : '';
foreach ( $replace_with as $job ) {
$jobs_actions = $jobs['job_actions'];
$replace_to = isset( $jobs['serialized_actions'] ) ? $jobs['serialized_actions'] : '';
foreach ( $jobs_actions as $job ) {
$this->current_job = $job;
$this->result = $this->action_process();
}
if ( 'item_image' === $this->type ) {
$this->post_content = str_replace( $replace_to, $this->result, wp_json_encode( $replace_with ) );
$this->field_content = str_replace( $replace_to, $this->result, wp_json_encode( $jobs_actions ) );
} else {
$this->post_content = str_replace( $replace_to, $this->result, $this->post_content );
$this->field_content = str_replace( $replace_to, $this->result, $this->field_content );
}
}
}
if ( empty( $actions ) && 'item_image' === $this->type ) {
return $default_value;
}
return $this->post_content;
return $this->field_content;
}

/**
Expand Down
27 changes: 16 additions & 11 deletions includes/admin/feedzy-rss-feeds-import.php
Original file line number Diff line number Diff line change
Expand Up @@ -1416,15 +1416,16 @@ private function run_job( $job, $max ) {
$item_date = date( get_option( 'date_format' ) . ' at ' . get_option( 'time_format' ), $item['item_date'] );
$item_date = $item['item_date_formatted'];

// Transform any structure like [[{"value":"[#item_title]"}]] to [#item_title].
$import_title = preg_replace( '/\[\[\{"value":"(\[#[^]]+\])"\}\]\]/', '$1', $import_title );

// Get translated item title.
$translated_title = '';
if ( $import_auto_translation && ( false !== strpos( $import_title, '[#translated_title]' ) || false !== strpos( $post_excerpt, '[#translated_title]' ) ) ) {
$translated_title = apply_filters( 'feedzy_invoke_auto_translate_services', $item['item_title'], '[#translated_title]', $import_translation_lang, $job, $language_code, $item );
}

$import_title = rawurldecode( $import_title );
$import_title = str_replace( PHP_EOL, "\r\n", $import_title );
$import_title = trim( $import_title );

$post_title = str_replace(
array(
'[#item_title]',
Expand All @@ -1447,6 +1448,10 @@ private function run_job( $job, $max ) {
$import_title
);

// Run all the actions stored for the embedded/serialized tags in the title field.
$title_action = $this->get_actions_runner( $post_title, 'item_title' );
$post_title = $title_action->run_action_job( $title_action->get_serialized_actions(), $translated_title, $job, $language_code, $item );

if ( $this->feedzy_is_business() ) {
$post_title = apply_filters( 'feedzy_parse_custom_tags', $post_title, $item_obj );
}
Expand Down Expand Up @@ -1546,8 +1551,8 @@ private function run_job( $job, $max ) {
$post_content = apply_filters( 'feedzy_invoke_services', $post_content, 'full_content', $full_content, $job );
}
// Item content action.
$content_action = $this->handle_content_actions( $post_content, 'item_content' );
$post_content = $content_action->get_tags();
$content_action = $this->get_actions_runner( $post_content, 'item_content' );
$post_content = $content_action->get_serialized_actions();
// Item content action process.
$post_content = $content_action->run_action_job( $post_content, $import_translation_lang, $job, $language_code, $item );
// Parse custom tags.
Expand Down Expand Up @@ -1832,7 +1837,7 @@ function( $term ) {
// Item image action.
$import_featured_img = rawurldecode( $import_featured_img );
$import_featured_img = trim( $import_featured_img );
$img_action = $this->handle_content_actions( $import_featured_img, 'item_image' );
$img_action = $this->get_actions_runner( $import_featured_img, 'item_image' );
// Item image action process.
$image_source_url = $img_action->run_action_job( $import_featured_img, $import_translation_lang, $job, $language_code, $item, $image_source_url );

Expand Down Expand Up @@ -2349,8 +2354,8 @@ public function render_magic_tags( $default, $tags, $type ) {
$disabled[ str_replace( ':disabled', '', $tag ) ] = $label;
continue;
}
if ( in_array( $type, array( 'import_post_content', 'import_post_featured_img' ), true ) ) {
if ( in_array( $tag, array( 'item_content', 'item_description', 'item_full_content', 'item_categories', 'item_image' ), true ) ) {
if ( in_array( $type, array( 'import_post_content', 'import_post_featured_img', 'import_post_title' ), true ) ) {
if ( in_array( $tag, array( 'item_content', 'item_description', 'item_full_content', 'item_categories', 'item_image', 'item_title' ), true ) ) {
$default .= '<a class="dropdown-item" href="#" data-field-name="' . $type . '" data-field-tag="' . $tag . '" data-action_popup="' . $tag . '">' . $label . ' <small>[#' . $tag . ']</small></a>';
continue;
}
Expand Down Expand Up @@ -2956,16 +2961,16 @@ private function wizard_import_feed() {
}

/**
* Handle item content actions.
* Get the content action runner used for processing the chained actions from the tags.
*
* @param string $actions Item content actions.
* @param string $type Action type.
* @return Feedzy_Rss_Feeds_Actions Instance of Feedzy_Rss_Feeds_Actions.
*/
public function handle_content_actions( $actions = '', $type = '' ) {
public function get_actions_runner( $actions = '', $type = '' ) {
$action_instance = Feedzy_Rss_Feeds_Actions::instance();
$action_instance->type = $type;
$action_instance->set_actions( $actions );
$action_instance->set_raw_serialized_actions( $actions );
$action_instance->set_settings( $this->settings );
return $action_instance;
}
Expand Down
4 changes: 2 additions & 2 deletions includes/gutenberg/build/block.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion includes/views/import-metabox-edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ class="dashicons dashicons-arrow-down-alt2"></span>
?>
</div>
</div>
<div class="fz-input-group-right">
<div class="fz-input-group-right fz-title-action-tags">
<div class="dropdown">
<button type="button" class="btn btn-outline-primary btn-add-fields dropdown-toggle" aria-haspopup="true" aria-expanded="false">
<?php esc_html_e( 'Insert Tag', 'feedzy-rss-feeds' ); ?> <span class="dashicons dashicons-plus-alt2"></span>
Expand Down
Loading

0 comments on commit 3a06eb9

Please sign in to comment.