12 Commits

Author SHA1 Message Date
554e2ba93e Merge pull request 'Adding Feature Request for single product highlight' (#7) from feature-single-product into main
All checks were successful
Create Release / build (push) Successful in 3s
Reviewed-on: #7
2025-05-18 17:27:06 +00:00
1955b14ac5 Adding Feature Request for single product highlight 2025-05-18 10:25:53 -07:00
10634e8f7e Merge pull request 'Remove Debug Code for version' (#5) from fix-debug-notice into main
All checks were successful
Create Release / build (push) Successful in 3s
Reviewed-on: #5
2025-04-22 20:47:06 +00:00
e94ef7b84b Remove Debug Code for version 2025-04-22 13:46:42 -07:00
db1d5f8945 Merge pull request 'Major Update to add auto-update features' (#4) from auto-update into main
All checks were successful
Create Release / build (push) Successful in 3s
Reviewed-on: #4
2025-04-22 20:38:32 +00:00
10ec29c30a Merge branch 'main' into auto-update 2025-04-22 20:37:39 +00:00
bf99f442ba Major Update to add auto-update features 2025-04-22 13:35:47 -07:00
0d0cb65633 Add workflow to update version number on release
All checks were successful
Create Release / build (push) Successful in 2s
2025-04-22 20:28:17 +00:00
c392af46e7 Merge pull request 'Update for FW switching to sending Binary instead of raw html' (#3) from Fix-fw-binary into main
All checks were successful
Create Release / build (push) Successful in 3s
Reviewed-on: #3
2025-04-22 17:50:27 +00:00
1bdae9a815 Update for FW switching to sending Binary instead of raw html 2025-04-22 10:48:19 -07:00
be7db52eec Update DOM crawler to resolver PHP warning
All checks were successful
Create Release / build (push) Successful in 3s
2025-01-26 12:30:03 -08:00
jknapp
059cc94063 Merge pull request 'updating docs and settings for release' (#1) from update-documentation into main
All checks were successful
Create Release / build (push) Successful in 2s
Reviewed-on: CyberCoveLLC/fourth-wall-embed-wp#1
2025-01-11 03:58:05 +00:00
6 changed files with 450 additions and 10 deletions

View File

@@ -14,6 +14,7 @@ jobs:
with:
username: ${{ secrets.CI_USER }}
password: ${{ secrets.CI_TOKEN }}
fetch-depth: 0 # Important: Fetch all history for commit messages
- name: Get version
id: get_version
@@ -24,6 +25,42 @@ jobs:
echo "version=$(date +'%Y.%m.%d-%H%M')" >> $GITHUB_OUTPUT
fi
# NEW STEP: Generate release notes from commits
- name: Generate release notes
id: release_notes
run: |
# Find the most recent tag
LATEST_TAG=$(git describe --tags --abbrev=0 --always 2>/dev/null || echo "none")
if [ "$LATEST_TAG" = "none" ]; then
# If no previous tag exists, get all commits
COMMITS=$(git log --pretty=format:"* %s (%h)" --no-merges)
else
# Get commits since the last tag
COMMITS=$(git log --pretty=format:"* %s (%h)" ${LATEST_TAG}..HEAD --no-merges)
fi
# Escape newlines and special characters for GitHub Actions
COMMITS="${COMMITS//'%'/'%25'}"
COMMITS="${COMMITS//$'\n'/'%0A'}"
COMMITS="${COMMITS//$'\r'/'%0D'}"
# Create release notes with header
NOTES="## What's New in ${{ steps.get_version.outputs.version }}%0A%0A"
NOTES+="$COMMITS"
# Output to GitHub Actions
echo "notes<<EOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Update plugin version
run: |
# Replace version placeholder with actual version
sed -i "s/Version: {auto_update_value_on_deploy}/Version: ${{ steps.get_version.outputs.version }}/" fw-store-embed.php
# Verify the change was made
grep "Version:" fw-store-embed.php
- name: Create ZIP archive
run: |
zip -r fourthwall-store-embed.zip . -x ".git/*" ".gitea/*"
@@ -34,5 +71,6 @@ jobs:
token: "${{ secrets.REPO_TOKEN }}"
title: Fourthwall-store-embed Release ${{ steps.get_version.outputs.version }}
tag_name: ${{ steps.get_version.outputs.version }}
body: "${{ steps.release_notes.outputs.notes }}"
files: |
fourthwall-store-embed.zip
fourthwall-store-embed.zip

View File

@@ -0,0 +1,49 @@
name: Update Plugin Version
on:
release:
types: [created, edited]
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get release tag
id: get_tag
run: echo "TAG=${GITEA_REF#refs/tags/}" >> $GITEA_ENV
- name: Update version in plugin file
run: |
# Replace version in main plugin file
sed -i "s/Version: .*/Version: ${{ env.TAG }}/" fw-store-embed.php
# Verify change
grep "Version:" fw-store-embed.php
- name: Commit changes
run: |
git config --local user.email "action@gitea.com"
git config --local user.name "Gitea Action"
git add fw-store-embed.php
git commit -m "Update version to ${{ env.TAG }}"
git push
- name: Create plugin zip
run: |
mkdir -p build
zip -r build/fourthwall-store-embed.zip . -x ".git/*" ".gitea/*" "build/*" "*.git*"
- name: Upload zip to release
uses: actions/upload-release-asset@v1
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
with:
upload_url: ${{ gitea.event.release.upload_url }}
asset_path: build/fourthwall-store-embed.zip
asset_name: fourthwall-store-embed.zip
asset_content_type: application/zip

View File

@@ -1,12 +1,29 @@
# fourth-wall-embed-wp
A WordPress Plugin to embed a fourth wall embed.
A WordPress Plugin to embed a Fourthwall store or individual products.
### How to use the plugin
* Download the latest release from [releases](https://repo.anhonesthost.net/CyberCoveLLC/fourth-wall-embed-wp/releases)
* Upload the Plugin to WordPress
* In the WordPress Dashboard, Navigate to the Fourthwall settings page, and paste your store url.
* On the page you want have your store displayed, add the shortcode ```[fourthwall]```
* In the WordPress Dashboard, Navigate to the Fourthwall settings page, and paste your store URL.
This is an early release of the plugin, and if you can think of things that could be improved or find any bugs, please open an issue on the repository.
#### Display your entire store
To display your entire Fourthwall store on a page, use the shortcode:
```[fourthwall]```
#### Display a single product
To display an individual product from your Fourthwall store, use the shortcode with the product URL:
```[fourthwall_single url="https://your-store.fourthwall.com/products/product-name"]```
You can also display the product description by setting the `show_description` attribute to "true":
```[fourthwall_single url="https://your-store.fourthwall.com/products/product-name" show_description="true"]```
### Requirements
* WordPress 6.0 or higher
* PHP 7.4 or higher
* Active Fourthwall store
This plugin allows you to integrate your Fourthwall store directly into your WordPress site, either showing your complete product collection or featuring specific products individually.
If you can think of things that could be improved or find any bugs, please open an issue on the repository.

View File

@@ -1,14 +1,15 @@
<?php
/**
* Plugin Name: Fourthwall Store Embed
* Plugin URL: https://cybercove.io/
* Plugin URL: https://darksideofperfection.com/
* Description: Embed Fourthwall Store in WordPress
* Version: 1.0.0.
* Version: {auto_update_value_on_deploy}
* Author: Joshua Knapp
* Text Domain: fourthwall_text_domain
*
*/
include("libs/self-update.php");
include("libs/settings.php");
include("libs/shortcode.php");
function set_fw_store_embed_css() {

177
libs/self-update.php Normal file
View File

@@ -0,0 +1,177 @@
<?php
/**
* Plugin update functionality
*/
// Don't execute directly
if (!defined('ABSPATH')) {
exit;
}
function fwembed_check_for_updates() {
// IMPORTANT: Get the main plugin file path correctly
$main_plugin_file = dirname(dirname(__FILE__)) . '/fw-store-embed.php';
$plugin_slug = plugin_basename($main_plugin_file);
// Get plugin data from main file
if (!function_exists('get_plugin_data')) {
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
}
// Get plugin data from main plugin file (not this file)
$plugin_data = get_plugin_data($main_plugin_file);
$current_version = !empty($plugin_data['Version']) ? $plugin_data['Version'] : '0.0.1';
// Debug data
$debug_data = [
'current_version' => $current_version,
'main_plugin_file' => $main_plugin_file,
'plugin_slug' => $plugin_slug,
'time' => current_time('mysql')
];
// URL to your Gitea releases API
$gitea_api_url = 'https://repo.anhonesthost.net/api/v1/repos/wp-plugins/fourth-wall-embed-wp/releases/latest';
// Use cURL to fetch release data
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $gitea_api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']);
curl_setopt($ch, CURLOPT_USERAGENT, 'WordPress/Fourth-Wall-Plugin-Updater');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
curl_close($ch);
if ($response) {
$release_info = json_decode($response);
// Get latest version from tag name
$latest_version = $release_info->tag_name;
// Simple version comparison - we want any update to trigger if version is different
if ($current_version != $latest_version) {
// Get the download URL for the ZIP
$download_url = null;
if (isset($release_info->assets) && is_array($release_info->assets)) {
foreach ($release_info->assets as $asset) {
if (isset($asset->name) && strpos($asset->name, '.zip') !== false) {
$download_url = $asset->browser_download_url;
break;
}
}
}
// Hook into WordPress update system
add_filter('site_transient_update_plugins', function($transient) use ($plugin_slug, $current_version, $latest_version, $download_url) {
// Initialize if needed
if (!is_object($transient)) {
$transient = new stdClass();
}
if (empty($transient->checked)) {
$transient->checked = [];
}
// Add our plugin to the checked list
$transient->checked[$plugin_slug] = $current_version;
if (empty($transient->response)) {
$transient->response = [];
}
// Add to the update list
$transient->response[$plugin_slug] = (object) [
'id' => 'fourthwall-store-embed',
'slug' => 'fourthwall-store-embed',
'plugin' => $plugin_slug,
'new_version' => $latest_version,
'url' => 'https://repo.anhonesthost.net/wp-plugins/fourth-wall-embed-wp',
'package' => $download_url,
'icons' => [],
'banners' => [],
'tested' => '6.8',
'requires' => '6.2',
'compatibility' => new stdClass(),
];
return $transient;
});
}
}
}
add_action('admin_init', 'fwembed_check_for_updates');
// Add plugin information for the details popup/changelog
add_filter('plugins_api', function($result, $action, $args) {
// Only handle requests for our plugin
$plugin_slug = 'fourthwall-store-embed'; // The slug is directory name
if ($action !== 'plugin_information' || !isset($args->slug) || $args->slug !== $plugin_slug) {
return $result;
}
// Get latest release info from Gitea API
$gitea_api_url = 'https://repo.anhonesthost.net/api/v1/repos/wp-plugins/fourth-wall-embed-wp/releases/latest';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $gitea_api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']);
curl_setopt($ch, CURLOPT_USERAGENT, 'WordPress/Fourth-Wall-Plugin-Updater');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
curl_close($ch);
if (!$response) {
return $result; // Keep default result if API call fails
}
$release_info = json_decode($response);
// Build plugin info object
$plugin_info = new stdClass();
$plugin_info->name = 'Fourthwall Store Embed';
$plugin_info->slug = $plugin_slug;
$plugin_info->version = $release_info->tag_name;
$plugin_info->author = '<a href="https://cybercove.io/">Joshua Knapp</a>';
$plugin_info->homepage = 'https://repo.anhonesthost.net/wp-plugins/fourth-wall-embed-wp';
$plugin_info->requires = '5.0';
$plugin_info->tested = '6.8';
$plugin_info->downloaded = 10;
$plugin_info->last_updated = $release_info->published_at;
// Format the release notes
$changelog = !empty($release_info->body) ? $release_info->body : 'No changelog provided for this release.';
// Handle empty changelog
if (empty(trim($changelog))) {
$changelog = "Version " . $release_info->tag_name . "\n\n" .
"Released on " . date('F j, Y', strtotime($release_info->published_at)) . "\n\n" .
"* Updated plugin files";
}
// Format sections for display
$plugin_info->sections = [
'description' => '<p>Embed Fourthwall Store in WordPress.</p>',
'changelog' => '<pre>' . esc_html($changelog) . '</pre>',
'installation' => '<p>Upload the plugin to your WordPress site and activate it.</p>'
];
// Download link
$download_url = null;
if (isset($release_info->assets) && is_array($release_info->assets)) {
foreach ($release_info->assets as $asset) {
if (isset($asset->name) && strpos($asset->name, '.zip') !== false) {
$download_url = $asset->browser_download_url;
break;
}
}
}
$plugin_info->download_link = $download_url;
return $plugin_info;
}, 10, 3);

View File

@@ -1,12 +1,53 @@
<?php
function fwembed_parse_html($url = null) {
if ($url === null) {
if ($url === null) {
throw new ValueError("Missing URL");
}
$ch = curl_init();
// More complete browser-like headers
$headers = [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.5',
'Connection: keep-alive',
'Upgrade-Insecure-Requests: 1',
'Cache-Control: max-age=0'
];
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Only if necessary for testing
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt'); // Store cookies
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt'); // Use cookies
$html_content = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return "Error fetching URL: " . $error;
}
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status_code == 403) {
curl_close($ch);
return "Access forbidden (403). The website may be blocking automated requests.";
}
curl_close($ch);
$html = null;
libxml_use_internal_errors(true);
$dom = new DOMDocument();
@$dom->loadHTML(file_get_contents($url));
@$dom->loadHTML(loadHTML5($html_content), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$dom->documentURI = $url;
$divs = $dom->getElementsByTagName('div');
foreach ($divs as $div) {
@@ -43,9 +84,14 @@ function fwembed_parse_html($url = null) {
$html = $html . '<div class="product-tile"><a class="product-link" target="_blank" href="' . $url . $linkHref . '">' . $productHTML . '</a></div>';
}
}
libxml_clear_errors();
return $html;
}
function loadHTML5($html) {
return '<!DOCTYPE html><html><body>' . $html . '</body></html>';
}
function fwembed_shortcode( $atts ) {
$options = get_option( 'fourthwall_settings_name' );
$value = isset( $options['fourth_url'] ) ? $options['fourth_url'] : 'https://fourthwall.com';
@@ -53,4 +99,116 @@ function fwembed_shortcode( $atts ) {
$store_render = '<div class="fw-store-parent">' . PHP_EOL . $store_html . PHP_EOL . '</div>';
return $store_render;
}
add_shortcode( 'fourthwall', 'fwembed_shortcode' );
function fwembed_parse_html_single($url = null, $show_description = false) {
if ($url === null) {
throw new ValueError("Missing URL");
}
$ch = curl_init();
// More complete browser-like headers
$headers = [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.5',
'Connection: keep-alive',
'Upgrade-Insecure-Requests: 1',
'Cache-Control: max-age=0'
];
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Only if necessary for testing
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt'); // Store cookies
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt'); // Use cookies
$html_content = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return "Error fetching URL: " . $error;
}
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status_code == 403) {
curl_close($ch);
return "Access forbidden (403). The website may be blocking automated requests.";
}
curl_close($ch);
$html = null;
libxml_use_internal_errors(true);
$dom = new DOMDocument();
@$dom->loadHTML(loadHTML5($html_content), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$dom->documentURI = $url;
$xpath = new DOMXPath($dom);
// Extract product information
$productTitle = $xpath->query('//h1[@class="product-info__title"]');
$productPrice = $xpath->query('//span[@class="product-info__price product-info__price--original"]');
$productImage = $xpath->query('//div[@data-gallery="gallery-slide"][1]//img[@class="gallery__image-object"]');
$productDesc = $xpath->query('//div[@class="product-info__description"]//div[@class="html-formatter"]');
if ($productTitle->length > 0 && $productImage->length > 0) {
$title = $productTitle->item(0)->textContent;
$price = $productPrice->length > 0 ? $productPrice->item(0)->textContent : '';
// Get image attributes
$imageNode = $productImage->item(0);
$imageSrc = $imageNode->getAttribute('src');
$imageAlt = $imageNode->getAttribute('alt');
// Build the HTML
$html = '<div class="product-tile">';
$html .= '<a class="product-link" target="_blank" href="' . $url . '">';
$html .= '<img class="tile__item tile__item--1" src="' . $imageSrc . '" alt="' . htmlspecialchars($imageAlt) . '">';
$html .= '<div class="tile__description">';
$html .= '<h3 class="tile__heading">' . htmlspecialchars(trim($title)) . '</h3>';
$html .= '<div class="tile__prices">';
$html .= '<span class="tile__price tile__price--original">' . trim($price) . '</span>';
$html .= '</div>';
$html .= '</div>';
$html .= '</a>';
// Add description if show_description is true
if ($show_description && $productDesc->length > 0) {
$description = $dom->saveHTML($productDesc->item(0));
$html .= '<div class="product-description">' . $description . '</div>';
}
$html .= '</div>';
}
libxml_clear_errors();
return $html;
}
function fwembed_single_shortcode($atts) {
$atts = shortcode_atts(
array(
'url' => '',
'show_description' => 'false',
),
$atts
);
if (empty($atts['url'])) {
return '<p>Error: URL is required for [fourthwall_single] shortcode</p>';
}
// Convert string 'true'/'false' to boolean
$show_description = filter_var($atts['show_description'], FILTER_VALIDATE_BOOLEAN);
$product_html = fwembed_parse_html_single($atts['url'], $show_description);
return '<div class="fw-single-product">' . PHP_EOL . $product_html . PHP_EOL . '</div>';
}
add_shortcode('fourthwall_single', 'fwembed_single_shortcode');
add_shortcode( 'fourthwall', 'fwembed_shortcode' );