Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ Customizations at WordPress relative to website libresign.coop
- Clone this repository at your plugin folder
- Enable this plugin

## Product next steps

WooCommerce products can define their own post-purchase action in **Product data → General**:

- Next-step button label
- Next-step URL
- Whether the link opens in a new tab

The action is shown on the order received page only after payment. Products without
a configured action do not show a product-specific next-step button.

### Configure deploy

- Go to Configurations of this plugin
Expand Down
266 changes: 265 additions & 1 deletion libresign-wp-customizations.php

@vitormattos vitormattos Jul 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best approach is to use the product note. It’s a default field for a product and it can support rich text. When we translate a product, the note also needs to be translated. Hardcoding the next steps would be a risky workaround.

Follow the suggested field to be used:

Image

If we add this PR as-is to the backlog, we can avoid approving a workaround that would generate a lot of code and extra work for a change that can be solved with a simple fix.

A more simple approach is to define a best place to display the product note.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more simple approach is to define a best place to display the product note.

I agree. It seems like the simplest way.

Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,161 @@ function libresign_get_account_help_url() {
return home_url( '/ajuda/' );
}

/**
* URL of the Nextcloud instance, shared with the WooCommerce integration plugin.
*/
function libresign_get_nextcloud_url() {
return trim( (string) get_option( 'nextcloud_api_host' ) );
}

/**
* Add configurable post-purchase next-step fields to WooCommerce products.
*/
function libresign_add_product_next_step_fields() {
echo '<div class="options_group">';

woocommerce_wp_text_input(
array(
'id' => '_libresign_next_step_label',
'label' => __( 'Next-step button label', 'libresign-wp-customizations' ),
'description' => __( 'Leave blank if this product has no product-specific next step.', 'libresign-wp-customizations' ),
'desc_tip' => true,
)
);

woocommerce_wp_text_input(
array(
'id' => '_libresign_next_step_url',
'label' => __( 'Next-step URL', 'libresign-wp-customizations' ),
'description' => __( 'Shown on the order received page after this product is paid for.', 'libresign-wp-customizations' ),
'desc_tip' => true,
'type' => 'url',
)
);

woocommerce_wp_checkbox(
array(
'id' => '_libresign_next_step_new_tab',
'label' => __( 'Open in a new tab', 'libresign-wp-customizations' ),
'description' => __( 'Open this product’s next step in a separate browser tab.', 'libresign-wp-customizations' ),
)
);

echo '</div>';
}
add_action( 'woocommerce_product_options_general_product_data', 'libresign_add_product_next_step_fields' );

/**
* Save a product's post-purchase next step.
*
* @param WC_Product $product Product being saved.
*/
function libresign_save_product_next_step_fields( $product ) {
$label = isset( $_POST['_libresign_next_step_label'] ) ? sanitize_text_field( wp_unslash( $_POST['_libresign_next_step_label'] ) ) : '';
$url = isset( $_POST['_libresign_next_step_url'] ) ? esc_url_raw( wp_unslash( $_POST['_libresign_next_step_url'] ) ) : '';
$new_tab = isset( $_POST['_libresign_next_step_new_tab'] ) ? 'yes' : 'no';

$product->update_meta_data( '_libresign_next_step_label', $label );
$product->update_meta_data( '_libresign_next_step_url', $url );
$product->update_meta_data( '_libresign_next_step_new_tab', $new_tab );
}
add_action( 'woocommerce_admin_process_product_object', 'libresign_save_product_next_step_fields' );

/**
* Get the next step configured for a product.
*
* @param WC_Product|int $product Product object or ID.
* @return array|null
*/
function libresign_get_product_next_step( $product ) {
$product = is_object( $product ) ? $product : wc_get_product( $product );

if ( ! $product ) {
return null;
}

$label = trim( (string) $product->get_meta( '_libresign_next_step_label', true ) );
$url = trim( (string) $product->get_meta( '_libresign_next_step_url', true ) );
$new_tab = 'yes' === $product->get_meta( '_libresign_next_step_new_tab', true );

if ( ( '' === $label || '' === $url ) && $product->get_parent_id() ) {
return libresign_get_product_next_step( $product->get_parent_id() );
}

if ( '' === $label || '' === $url ) {
return null;
}

return apply_filters(
'libresign_product_next_step',
array(
'label' => $label,
'url' => $url,
'new_tab' => $new_tab,
),
$product
);
}

/**
* Preserve the configured next step on the order item.
*
* This keeps existing orders stable if a product's next step changes later.
*
* @param WC_Order_Item_Product $item Order item being created.
* @param string $item_key Cart item key.
* @param array $values Cart item values.
*/
function libresign_store_product_next_step_on_order_item( $item, $item_key, $values ) {
unset( $item_key );

$product = isset( $values['data'] ) ? $values['data'] : null;
$next_step = libresign_get_product_next_step( $product );

if ( ! $next_step ) {
return;
}

$item->add_meta_data( '_libresign_next_step_label', $next_step['label'], true );
$item->add_meta_data( '_libresign_next_step_url', $next_step['url'], true );
$item->add_meta_data( '_libresign_next_step_new_tab', $next_step['new_tab'] ? 'yes' : 'no', true );
}
add_action( 'woocommerce_checkout_create_order_line_item', 'libresign_store_product_next_step_on_order_item', 10, 3 );

/**
* Get the unique product-specific next steps for an order.
*
* @param WC_Order $order Order being displayed.
* @return array
*/
function libresign_get_order_next_steps( $order ) {
$next_steps = array();

foreach ( $order->get_items() as $item ) {
$label = trim( (string) $item->get_meta( '_libresign_next_step_label', true ) );
$url = trim( (string) $item->get_meta( '_libresign_next_step_url', true ) );

if ( '' !== $label && '' !== $url ) {
$next_step = array(
'label' => $label,
'url' => $url,
'new_tab' => 'yes' === $item->get_meta( '_libresign_next_step_new_tab', true ),
);
} else {
$next_step = libresign_get_product_next_step( $item->get_product() );
}

if ( ! $next_step ) {
continue;
}

$key = md5( $next_step['label'] . '|' . $next_step['url'] . '|' . (int) $next_step['new_tab'] );
$next_steps[ $key ] = $next_step;
}

return apply_filters( 'libresign_order_next_steps', array_values( $next_steps ), $order );
}

/**
* Render a CTA on every customer account screen that points to the Nextcloud instance.
*/
Expand All @@ -613,7 +768,7 @@ function libresign_render_nextcloud_account_button() {
return;
}

$nextcloud_host = trim( (string) get_option( 'nextcloud_api_host' ) );
$nextcloud_host = libresign_get_nextcloud_url();

if ( '' === $nextcloud_host ) {
return;
Expand All @@ -631,6 +786,115 @@ function libresign_render_nextcloud_account_button() {
}
add_action( 'woocommerce_before_account_navigation', 'libresign_render_nextcloud_account_button', 20 );

/**
* Store and read the WooCommerce Subscriptions thank you message, so it can be moved
* into the next steps block instead of being printed on its own.
*
* @param string|null $message Message to store. Omit to read the stored one.
*/
function libresign_thank_you_subscription_message( $message = null ) {
static $stored = '';

if ( null !== $message ) {
$stored = (string) $message;
}

return $stored;
}

add_filter( 'woocommerce_subscriptions_thank_you_message', function ( $message ) {
libresign_thank_you_subscription_message( $message );

return '';
}, 99 );

/**
* Print the subscription notice with the same allowlist the Subscriptions plugin applies to it.
*/
function libresign_print_thank_you_subscription_message( $message ) {
if ( '' === $message ) {
return;
}

echo wp_kses(
$message,
array(
'a' => array(
'href' => array(),
'title' => array(),
),
'p' => array(),
'em' => array(),
'strong' => array(),
)
);
}

/**
* Render the subscription status notice and relevant next steps on the order received page.
*
* @param int $order_id Order being displayed.
*/
function libresign_render_thank_you_next_steps( $order_id = 0 ) {
$subscription_message = libresign_thank_you_subscription_message();
$order = $order_id ? wc_get_order( $order_id ) : false;
$account_url = '';
$product_next_steps = array();

libresign_thank_you_subscription_message( '' );

if ( $order && is_user_logged_in() && $order->get_user_id() === get_current_user_id() ) {
$account_url = (string) wc_get_page_permalink( 'myaccount' );

if ( $order->is_paid() ) {
$product_next_steps = libresign_get_order_next_steps( $order );
}
}

if ( '' === $account_url && empty( $product_next_steps ) ) {
libresign_print_thank_you_subscription_message( $subscription_message );

return;
}

echo '<div class="libresign-thankyou-next-steps" style="margin-top: 1.5rem; padding: 1rem; border: 1px solid currentColor; border-radius: 0.75rem;">';

printf(
'<p style="margin: 0 0 1rem 0;">%s</p>',
esc_html__( 'What would you like to do next?', 'libresign-wp-customizations' )
);

if ( '' !== $subscription_message ) {
echo '<div class="libresign-thankyou-next-steps__notice" style="margin: 0 0 1rem 0;">';
libresign_print_thank_you_subscription_message( $subscription_message );
echo '</div>';
}

echo '<p style="margin: 0; display: flex; flex-wrap: wrap; gap: 0.75rem;">';

if ( '' !== $account_url ) {
printf(
'<a class="wp-block-button__link wp-element-button is-style-outline" href="%s">%s</a>',
esc_url( $account_url ),
esc_html__( 'Go to your dashboard', 'libresign-wp-customizations' )
);
}

foreach ( $product_next_steps as $next_step ) {
$target_attributes = $next_step['new_tab'] ? ' target="_blank" rel="noopener noreferrer"' : '';

printf(
'<a class="wp-block-button__link wp-element-button" href="%s"%s>%s</a>',
esc_url( $next_step['url'] ),
$target_attributes,
esc_html( $next_step['label'] )
);
}

echo '</p></div>';
}
add_action( 'woocommerce_thankyou', 'libresign_render_thank_you_next_steps', 11 );

/**
* Confirmation strings for each subscription status change that requires an extra step.
*/
Expand Down