Compare commits
2 commits
4987c4830f
...
4489e705b8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4489e705b8 | ||
|
|
ade0ed1a67 |
21 changed files with 611 additions and 154 deletions
|
|
@ -7,7 +7,10 @@
|
||||||
"Bash(find:*)",
|
"Bash(find:*)",
|
||||||
"Bash(curl:*)",
|
"Bash(curl:*)",
|
||||||
"WebFetch(domain:snipcart.com)",
|
"WebFetch(domain:snipcart.com)",
|
||||||
"Bash(grep:*)"
|
"Bash(grep:*)",
|
||||||
|
"Bash(npm run build:*)",
|
||||||
|
"Bash(php test-shopify.php:*)",
|
||||||
|
"WebFetch(domain:getkirby.com)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
Title: Éclairages : 12 entretiens et analyses sur les violences d'État
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Uuid: gzshayl6xoefrnsz
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
Title: Éclairages : 12 entretiens et analyses sur les violences d’État
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Shopifyhandle: eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Uuid: gzshayl6xoefrnsz
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
Title: T-shirt Index
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Uuid: qq27mjjpethsvnwp
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
Title: T-shirt Index
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Shopifyhandle: t-shirt-index-01
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Uuid: qq27mjjpethsvnwp
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
title: Site
|
title: Site
|
||||||
|
|
||||||
sections:
|
sections:
|
||||||
pages:
|
shopify:
|
||||||
type: pages
|
type: fields
|
||||||
headline:
|
fields:
|
||||||
en: Products
|
shopifyRefreshButton:
|
||||||
fr: Produits
|
type: shopify-refresh
|
||||||
template: product
|
label:
|
||||||
sortBy: title asc
|
en: Shopify Products
|
||||||
info: "{{ page.stock }} en stock"
|
fr: Produits Shopify
|
||||||
layout: cardlets
|
|
||||||
image:
|
|
||||||
query: page.files.first
|
|
||||||
cover: true
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,81 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/shopify.php';
|
||||||
|
|
||||||
|
use Kirby\Cms\Page;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'debug' => true,
|
'debug' => true,
|
||||||
|
|
||||||
'languages' => true,
|
'languages' => true,
|
||||||
|
|
||||||
|
'cache' => [
|
||||||
|
'shopify' => true
|
||||||
|
],
|
||||||
|
|
||||||
|
'routes' => [
|
||||||
|
// French product pages (default)
|
||||||
|
[
|
||||||
|
'pattern' => '(:any)',
|
||||||
|
'action' => function($slug) {
|
||||||
|
// Skip known pages
|
||||||
|
if (in_array($slug, ['home', 'error', 'thanks'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = getShopifyProducts();
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
if ($product['handle'] === $slug) {
|
||||||
|
return Page::factory([
|
||||||
|
'slug' => $product['handle'],
|
||||||
|
'template' => 'product',
|
||||||
|
'parent' => site()->homePage(),
|
||||||
|
'content' => [
|
||||||
|
'title' => $product['title'],
|
||||||
|
'shopifyHandle' => $product['handle'],
|
||||||
|
'uuid' => $product['id']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a product, let Kirby handle normally
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// English product pages
|
||||||
|
[
|
||||||
|
'pattern' => 'en/(:any)',
|
||||||
|
'action' => function($slug) {
|
||||||
|
// Skip known pages
|
||||||
|
if (in_array($slug, ['home', 'error', 'thanks'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = getShopifyProducts();
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
if ($product['handle'] === $slug) {
|
||||||
|
return Page::factory([
|
||||||
|
'slug' => $product['handle'],
|
||||||
|
'template' => 'product',
|
||||||
|
'parent' => site()->homePage(),
|
||||||
|
'content' => [
|
||||||
|
'title' => $product['title'],
|
||||||
|
'shopifyHandle' => $product['handle'],
|
||||||
|
'uuid' => $product['id']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a product, let Kirby handle normally
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
'thumbs' => [
|
'thumbs' => [
|
||||||
'quality' => 85,
|
'quality' => 85,
|
||||||
'format' => 'webp',
|
'format' => 'webp',
|
||||||
|
|
@ -35,117 +106,4 @@ return [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'routes' => [
|
|
||||||
// SNIPCART ROUTES - Désactivées, voir assets/snipcart-archive/README.md pour restauration
|
|
||||||
/*
|
|
||||||
[
|
|
||||||
'pattern' => '(:any)/validate.json',
|
|
||||||
'method' => 'GET',
|
|
||||||
'action' => function ($slug) {
|
|
||||||
$page = page($slug);
|
|
||||||
|
|
||||||
if (!$page || $page->intendedTemplate() !== 'product') {
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
http_response_code(404);
|
|
||||||
echo json_encode(['error' => 'Product not found']);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer le stock actuel
|
|
||||||
$stock = (int) $page->stock()->value();
|
|
||||||
|
|
||||||
// Préparer la réponse JSON pour Snipcart
|
|
||||||
$response = [
|
|
||||||
'id' => $page->slug(),
|
|
||||||
'price' => (float) $page->price()->value(),
|
|
||||||
'url' => $page->url() . '/validate.json',
|
|
||||||
'name' => $page->title()->value(),
|
|
||||||
'description' => $page->description()->value(),
|
|
||||||
'image' => $page->images()->first() ? $page->images()->first()->url() : '',
|
|
||||||
'inventory' => $stock,
|
|
||||||
'stock' => $stock
|
|
||||||
];
|
|
||||||
|
|
||||||
// Ajouter les options si disponibles
|
|
||||||
if ($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()) {
|
|
||||||
$values = $page->optionValues()->split(',');
|
|
||||||
$trimmedValues = array_map('trim', $values);
|
|
||||||
$snipcartOptions = implode('|', $trimmedValues);
|
|
||||||
|
|
||||||
$response['customFields'] = [
|
|
||||||
[
|
|
||||||
'name' => $page->optionLabel()->value(),
|
|
||||||
'options' => $snipcartOptions,
|
|
||||||
'required' => true
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
echo json_encode($response);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'pattern' => 'snipcart-webhook',
|
|
||||||
'method' => 'POST',
|
|
||||||
'action' => function () {
|
|
||||||
// Webhook handler pour Snipcart
|
|
||||||
// Vérifie la signature et décrémente le stock
|
|
||||||
|
|
||||||
$requestBody = file_get_contents('php://input');
|
|
||||||
$event = json_decode($requestBody, true);
|
|
||||||
|
|
||||||
// Vérifier la signature Snipcart (à implémenter avec la clé secrète)
|
|
||||||
// $signature = $_SERVER['HTTP_X_SNIPCART_REQUESTTOKEN'] ?? '';
|
|
||||||
|
|
||||||
if (!$event || !isset($event['eventName'])) {
|
|
||||||
return Response::json(['error' => 'Invalid request'], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gérer l'événement order.completed
|
|
||||||
if ($event['eventName'] === 'order.completed') {
|
|
||||||
$order = $event['content'] ?? null;
|
|
||||||
|
|
||||||
if ($order && isset($order['items'])) {
|
|
||||||
// Impersonate pour avoir les permissions d'écriture
|
|
||||||
kirby()->impersonate('kirby');
|
|
||||||
|
|
||||||
foreach ($order['items'] as $item) {
|
|
||||||
$productId = $item['id'] ?? null;
|
|
||||||
$quantity = $item['quantity'] ?? 0;
|
|
||||||
|
|
||||||
if ($productId && $quantity > 0) {
|
|
||||||
// Trouver le produit par son slug
|
|
||||||
$products = site()->index()->filterBy('intendedTemplate', 'product');
|
|
||||||
|
|
||||||
foreach ($products as $product) {
|
|
||||||
if ($product->slug() === $productId) {
|
|
||||||
// Décrémenter le stock
|
|
||||||
$currentStock = (int) $product->stock()->value();
|
|
||||||
$newStock = max(0, $currentStock - $quantity);
|
|
||||||
|
|
||||||
// Mettre à jour le stock
|
|
||||||
try {
|
|
||||||
$product->update([
|
|
||||||
'stock' => $newStock
|
|
||||||
]);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// Log l'erreur mais continue le traitement
|
|
||||||
error_log('Webhook stock update error: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response::json(['status' => 'success'], 200);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
*/
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
81
site/config/shopify.php
Normal file
81
site/config/shopify.php
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les produits Shopify avec cache (TTL 1h)
|
||||||
|
*/
|
||||||
|
function getShopifyProducts(): array
|
||||||
|
{
|
||||||
|
$cache = kirby()->cache('shopify');
|
||||||
|
$products = $cache->get('products');
|
||||||
|
|
||||||
|
if ($products === null) {
|
||||||
|
$products = fetchShopifyProducts();
|
||||||
|
$cache->set('products', $products, 60); // Cache 60 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appel direct à l'API Shopify Storefront
|
||||||
|
*/
|
||||||
|
function fetchShopifyProducts(): array
|
||||||
|
{
|
||||||
|
$domain = 'nv7cqv-bu.myshopify.com';
|
||||||
|
$token = 'dec3d35a2554384d149c72927d1cfd1b';
|
||||||
|
$apiVersion = '2026-01';
|
||||||
|
$endpoint = "https://{$domain}/api/{$apiVersion}/graphql.json";
|
||||||
|
|
||||||
|
$query = '
|
||||||
|
query getAllProducts {
|
||||||
|
products(first: 250, sortKey: TITLE) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
handle
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
';
|
||||||
|
|
||||||
|
$ch = curl_init($endpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'X-Shopify-Storefront-Access-Token: ' . $token
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||||
|
'query' => $query
|
||||||
|
]));
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
error_log("Shopify API error: HTTP {$httpCode}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
|
||||||
|
if (isset($data['errors'])) {
|
||||||
|
error_log("Shopify API GraphQL errors: " . json_encode($data['errors']));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$products = [];
|
||||||
|
foreach ($data['data']['products']['edges'] as $edge) {
|
||||||
|
$node = $edge['node'];
|
||||||
|
$products[] = [
|
||||||
|
'id' => $node['id'],
|
||||||
|
'handle' => $node['handle'],
|
||||||
|
'title' => $node['title']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
20
site/plugins/shopify-refresh-button/.editorconfig
Normal file
20
site/plugins/shopify-refresh-button/.editorconfig
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# This file is for unifying the coding style for different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.php]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.md,*.txt]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
[composer.json]
|
||||||
|
indent_size = 4
|
||||||
11
site/plugins/shopify-refresh-button/.gitattributes
vendored
Normal file
11
site/plugins/shopify-refresh-button/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Note: You need to uncomment the lines you want to use; the other lines can be deleted
|
||||||
|
|
||||||
|
# Git
|
||||||
|
# .gitattributes export-ignore
|
||||||
|
# .gitignore export-ignore
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
# /.coveralls.yml export-ignore
|
||||||
|
# /.travis.yml export-ignore
|
||||||
|
# /phpunit.xml.dist export-ignore
|
||||||
|
# /tests/ export-ignore
|
||||||
14
site/plugins/shopify-refresh-button/.gitignore
vendored
Normal file
14
site/plugins/shopify-refresh-button/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# npm modules
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Parcel cache folder
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Composer files
|
||||||
|
/vendor
|
||||||
|
|
||||||
|
# kirbyup temp development entry
|
||||||
|
/index.dev.mjs
|
||||||
21
site/plugins/shopify-refresh-button/LICENSE.md
Executable file
21
site/plugins/shopify-refresh-button/LICENSE.md
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <Year> <Your Name>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
117
site/plugins/shopify-refresh-button/README.md
Executable file
117
site/plugins/shopify-refresh-button/README.md
Executable file
|
|
@ -0,0 +1,117 @@
|
||||||
|
# Kirby Pluginkit: Example plugin for Kirby
|
||||||
|
|
||||||
|
> Variant "Panel plugin setup"
|
||||||
|
|
||||||
|
This is a boilerplate for a Kirby Panel plugin that can be installed via all three [supported installation methods](https://getkirby.com/docs/guide/plugins/plugin-setup-basic#the-three-plugin-installation-methods).
|
||||||
|
|
||||||
|
You can find a list of Pluginkit variants on the [`master` branch](https://github.com/getkirby/pluginkit/tree/master).
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## How to use the Pluginkit
|
||||||
|
|
||||||
|
1. Fork this repository
|
||||||
|
2. Change the plugin name and description in the `composer.json`
|
||||||
|
3. Change the plugin name in the `index.php` and `src/index.js`
|
||||||
|
4. Change the license if you don't want to publish under MIT
|
||||||
|
5. Add your plugin code to the `index.php` and `src/index.js`
|
||||||
|
6. Update this `README` with instructions for your plugin
|
||||||
|
|
||||||
|
### Install the development and build setup
|
||||||
|
|
||||||
|
We use [kirbyup](https://github.com/johannschopplich/kirbyup) for the development and build setup.
|
||||||
|
|
||||||
|
You can start developing directly. kirbyup will be fetched remotely with your first `npm run` command, which may take a short amount of time.
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
You can start the dev process with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically update the `index.js` and `index.css` of your plugin as soon as you make changes.
|
||||||
|
Reload the Panel to see your code changes reflected.
|
||||||
|
|
||||||
|
With kirbyup 2.0.0+ and Kirby 3.7.4+ you can alternatively use hot module reloading (HMR):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start a development server that updates the page as soon as you make changes. Some updates are instant, like CSS or Vue template changes, others require a reload of the page, which happens automatically.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The live reload functionality requires top level await, [which is only supported in modern browsers](https://caniuse.com/mdn-javascript_operators_await_top_level). If you're developing in older browsers, use `npm run dev` and reload the page manually to see changes.
|
||||||
|
|
||||||
|
### Production
|
||||||
|
|
||||||
|
As soon as you are happy with your plugin, you should build the final version with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically create a minified and optimized version of your `index.js` and `index.css`
|
||||||
|
which you can ship with your plugin.
|
||||||
|
|
||||||
|
We have a tutorial on how to build your own plugin based on the Pluginkit [in the Kirby documentation](https://getkirby.com/docs/guide/plugins/plugin-setup-basic).
|
||||||
|
|
||||||
|
### Build reproducibility
|
||||||
|
|
||||||
|
While kirbyup will stay backwards compatible, exact build reproducibility may be of importance to you. If so, we recommend to target a specific package version, rather than using npx:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "kirbyup src/index.js --watch",
|
||||||
|
"build": "kirbyup src/index.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"kirbyup": "^3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
What follows is an example README for your plugin.
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Download
|
||||||
|
|
||||||
|
Download and copy this repository to `/site/plugins/{{ plugin-name }}`.
|
||||||
|
|
||||||
|
### Git submodule
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git submodule add https://github.com/{{ your-name }}/{{ plugin-name }}.git site/plugins/{{ plugin-name }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require {{ your-name }}/{{ plugin-name }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
*Additional instructions on how to configure the plugin (e.g. blueprint setup, config options, etc.)*
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
*Document the options and APIs that this plugin offers*
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
*Add instructions on how to help working on the plugin (e.g. npm setup, Composer dev dependencies, etc.)*
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [Your Name](https://github.com/ghost)
|
||||||
18
site/plugins/shopify-refresh-button/SECURITY.md
Normal file
18
site/plugins/shopify-refresh-button/SECURITY.md
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
*Use this section to tell people about which versions of your project are currently being supported with security updates.*
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 5.1.x | :white_check_mark: |
|
||||||
|
| 5.0.x | :x: |
|
||||||
|
| 4.0.x | :white_check_mark: |
|
||||||
|
| < 4.0 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
*Use this section to tell people how to report a vulnerability.*
|
||||||
|
|
||||||
|
*Tell them where to go, how often they can expect to get an update on a reported vulnerability, what to expect if the vulnerability is accepted or declined, etc.*
|
||||||
21
site/plugins/shopify-refresh-button/composer.json
Executable file
21
site/plugins/shopify-refresh-button/composer.json
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "getkirby/pluginkit",
|
||||||
|
"description": "Kirby Example Plugin",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "kirby-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Your Name",
|
||||||
|
"email": "you@example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"getkirby/composer-installer": "^1.1"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"getkirby/composer-installer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
site/plugins/shopify-refresh-button/composer.lock
generated
Normal file
66
site/plugins/shopify-refresh-button/composer.lock
generated
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "37a8e61308b9b6f49cb9835f477f0c64",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "getkirby/composer-installer",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/getkirby/composer-installer.git",
|
||||||
|
"reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d",
|
||||||
|
"reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"composer-plugin-api": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"composer/composer": "^1.8 || ^2.0"
|
||||||
|
},
|
||||||
|
"type": "composer-plugin",
|
||||||
|
"extra": {
|
||||||
|
"class": "Kirby\\ComposerInstaller\\Plugin"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Kirby\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins",
|
||||||
|
"homepage": "https://getkirby.com",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/getkirby/composer-installer/issues",
|
||||||
|
"source": "https://github.com/getkirby/composer-installer/tree/1.2.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://getkirby.com/buy",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2020-12-28T12:54:39+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
||||||
2
site/plugins/shopify-refresh-button/index.js
Normal file
2
site/plugins/shopify-refresh-button/index.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
(function(){"use strict";function l(n,r,t,e,o,a,s,f){var i=typeof n=="function"?n.options:n;return r&&(i.render=r,i.staticRenderFns=t,i._compiled=!0),{exports:n,options:i}}const p={__name:"ShopifyRefreshButton",props:{products:{type:Array,default:()=>[]}},setup(n){const r=n,t=Vue.ref("Synchroniser depuis Shopify"),e=Vue.ref("refresh"),o=Vue.ref("aqua-icon"),a=Vue.ref(!1),s=Vue.ref([]);Vue.onMounted(()=>{s.value=r.products||[]});const f=Vue.computed(()=>{const u=s.value.length;return`${u} produit${u>1?"s":""} Shopify en cache`}),i=Vue.computed(()=>s.value.length===0?"Aucun produit trouvé. Cliquez sur 'Rafraîchir Shopify' pour récupérer les produits.":s.value.map(u=>`• ${u.title}<br/>`).join(`
|
||||||
|
`));async function y(){a.value=!0,e.value="loader",o.value="orange-icon",t.value="En cours…";try{const c=await(await fetch("/shopify/refresh-cache.json",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(c.status==="error")throw new Error(c.message);s.value=c.products||[],t.value=`Terminé - ${c.count} produit${c.count>1?"s":""}`,e.value="check",o.value="green-icon",setTimeout(()=>{t.value="Synchroniser depuis Shopify",e.value="refresh",o.value="aqua-icon",a.value=!1},3e3)}catch(u){console.error(u),t.value="Erreur",e.value="alert",o.value="red-icon",setTimeout(()=>{t.value="Synchroniser depuis Shopify",e.value="refresh",o.value="aqua-icon",a.value=!1},3e3)}}return{__sfc:!0,props:r,text:t,icon:e,theme:o,isProcessing:a,currentProducts:s,infoLabel:f,infoText:i,refreshCache:y}}};var h=function(){var r=this,t=r._self._c,e=r._self._setupProxy;return t("div",[t("k-info-field",{attrs:{label:e.infoLabel,text:e.infoText,theme:"info"}}),t("k-button",{staticStyle:{"margin-top":"1rem"},attrs:{theme:e.theme,variant:"dimmed",icon:e.icon,title:"Synchroniser le cache des produits Shopify",disabled:e.isProcessing},on:{click:function(o){return e.refreshCache()}}},[r._v(" "+r._s(e.text)+" ")])],1)},d=[],v=l(p,h,d);const m=v.exports;window.panel.plugin("index/shopify-refresh-button",{fields:{"shopify-refresh":m}})})();
|
||||||
46
site/plugins/shopify-refresh-button/index.php
Executable file
46
site/plugins/shopify-refresh-button/index.php
Executable file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
Kirby::plugin('index/shopify-refresh-button', [
|
||||||
|
'fields' => [
|
||||||
|
'shopify-refresh' => [
|
||||||
|
'props' => [
|
||||||
|
'products' => function() {
|
||||||
|
return getShopifyProducts();
|
||||||
|
}
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'routes' => [
|
||||||
|
[
|
||||||
|
'pattern' => 'shopify/refresh-cache.json',
|
||||||
|
'method' => 'POST',
|
||||||
|
'action' => function() {
|
||||||
|
if (!kirby()->user()) {
|
||||||
|
return [
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Unauthorized'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
kirby()->cache('shopify')->flush();
|
||||||
|
|
||||||
|
$products = fetchShopifyProducts();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Cache Shopify rafraîchi avec succès',
|
||||||
|
'count' => count($products),
|
||||||
|
'products' => $products
|
||||||
|
];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return [
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Erreur lors du rafraîchissement du cache',
|
||||||
|
'details' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
7
site/plugins/shopify-refresh-button/package.json
Normal file
7
site/plugins/shopify-refresh-button/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "npx -y kirbyup src/index.js --watch",
|
||||||
|
"serve": "npx -y kirbyup serve src/index.js",
|
||||||
|
"build": "npx -y kirbyup src/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<k-info-field :label="infoLabel" :text="infoText" theme="info" />
|
||||||
|
|
||||||
|
<k-button
|
||||||
|
style="margin-top: 1rem"
|
||||||
|
:theme="theme"
|
||||||
|
variant="dimmed"
|
||||||
|
:icon="icon"
|
||||||
|
title="Synchroniser le cache des produits Shopify"
|
||||||
|
@click="refreshCache()"
|
||||||
|
:disabled="isProcessing"
|
||||||
|
>
|
||||||
|
{{ text }}
|
||||||
|
</k-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
products: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = ref("Synchroniser depuis Shopify");
|
||||||
|
const icon = ref("refresh");
|
||||||
|
const theme = ref("aqua-icon");
|
||||||
|
const isProcessing = ref(false);
|
||||||
|
const currentProducts = ref([]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
currentProducts.value = props.products || [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const infoLabel = computed(() => {
|
||||||
|
const count = currentProducts.value.length;
|
||||||
|
return `${count} produit${count > 1 ? "s" : ""} Shopify en cache`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const infoText = computed(() => {
|
||||||
|
if (currentProducts.value.length === 0) {
|
||||||
|
return "Aucun produit trouvé. Cliquez sur 'Rafraîchir Shopify' pour récupérer les produits.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentProducts.value.map((p) => `• ${p.title}<br/>`).join("\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function refreshCache() {
|
||||||
|
isProcessing.value = true;
|
||||||
|
icon.value = "loader";
|
||||||
|
theme.value = "orange-icon";
|
||||||
|
text.value = "En cours…";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch("/shopify/refresh-cache.json", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = await res.json();
|
||||||
|
|
||||||
|
if (json.status === "error") {
|
||||||
|
throw new Error(json.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProducts.value = json.products || [];
|
||||||
|
text.value = `Terminé - ${json.count} produit${json.count > 1 ? "s" : ""}`;
|
||||||
|
icon.value = "check";
|
||||||
|
theme.value = "green-icon";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
text.value = "Synchroniser depuis Shopify";
|
||||||
|
icon.value = "refresh";
|
||||||
|
theme.value = "aqua-icon";
|
||||||
|
isProcessing.value = false;
|
||||||
|
}, 3000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
text.value = "Erreur";
|
||||||
|
icon.value = "alert";
|
||||||
|
theme.value = "red-icon";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
text.value = "Synchroniser depuis Shopify";
|
||||||
|
icon.value = "refresh";
|
||||||
|
theme.value = "aqua-icon";
|
||||||
|
isProcessing.value = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
7
site/plugins/shopify-refresh-button/src/index.js
Executable file
7
site/plugins/shopify-refresh-button/src/index.js
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import ShopifyRefreshButton from "./components/ShopifyRefreshButton.vue";
|
||||||
|
|
||||||
|
window.panel.plugin("index/shopify-refresh-button", {
|
||||||
|
fields: {
|
||||||
|
"shopify-refresh": ShopifyRefreshButton
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue