├── .github
└── workflows
│ └── tests.yml
├── .gitignore
├── .nvmrc
├── .wp-env.json
├── README.md
├── classes
├── Hooks.php
├── Plugin.php
├── Rest
│ ├── Api.php
│ └── Controllers
│ │ ├── Controller.php
│ │ └── Settings.php
├── Settings.php
├── SettingsPage.php
└── index.php
├── composer.json
├── composer.lock
├── makeZip.js
├── package-lock.json
├── package.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── pm2-modern-plugin.php
├── src
├── api
│ └── useSettings.js
├── block
│ ├── block.json
│ ├── edit.js
│ ├── editor.scss
│ ├── index.js
│ ├── save.js
│ └── style.scss
├── components
│ ├── ApiKeyField.js
│ └── RunOnce.js
├── editor.scss
├── index.php
└── settings
│ ├── index.js
│ ├── otherTab.js
│ ├── settingsTab.js
│ └── style.css
├── tests
├── WordPress
│ ├── SettingsTest.php
│ ├── SetupTest.php
│ └── TestCase.php
├── bootstrap.php
├── index.php
└── wp-config.php
└── webpack.config.js
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | # Run on
4 | on:
5 | push:
6 | # any branch
7 | branches:
8 | - '*'
9 | jobs:
10 | tests:
11 | name: Install And Test
12 | runs-on: ubuntu-20.04
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup Node
18 | uses: actions/setup-node@v3
19 | with:
20 | cache: 'npm'
21 | node-version-file: '.nvmrc'
22 |
23 | - name: Setup PHP and Composer
24 | uses: shivammathur/setup-php@v2
25 | with:
26 | php-version: '7.4'
27 | tools: composer:v2
28 |
29 | - name: Install NPM dependencies
30 | run: npm install
31 |
32 | - name: Start the Docker testing environment
33 | run: npm run env start --xdebug=coverage
34 |
35 | - name: Ensure uploads dir exists first
36 | run: mkdir -p wordpress/wp-content/uploads
37 |
38 | - name: Test
39 | run: npm run test:php
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 | /npm-debug.log
4 | /vendor/
5 | /dist/
6 | /tests/logs/
7 | /wordpress/
8 | build
9 | .phpunit.result.cache
10 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 17
2 |
--------------------------------------------------------------------------------
/.wp-env.json:
--------------------------------------------------------------------------------
1 | {
2 | "core": "WordPress/WordPress",
3 | "plugins": [
4 | "."
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PLUGIN_NAME
2 |
3 | Put Your Description Here
4 |
5 | [](https://pluginmachine.com)
6 |
7 |
8 | ## Developopment
9 |
10 | - Clone
11 | - git@github.com:GITHUB_ORG/GITHUB_REPO.git
12 | - Install
13 | - `npm i`
14 | - Installs with npm and composer
15 | - Start environment
16 | - `npm run env start`
17 | - Uses [@wordpres/env](https://www.npmjs.com/package/@wordpress/env)
18 | - [Requires Docker](https://www.docker.com/products/docker-desktop/)
19 | - Test PHP
20 | - `npm run test:php`
21 | - Format JS, CSS and PHP
22 | - `npm run format`
23 | - Run linter, without fixing code
24 | - `npm run lint`
25 | - Build JS/CSS
26 | - `npm run build`
27 | - Create zip
28 | - `npm run zip`
29 | - Installs with composer optimized
30 | - Builds CSS/JS/Blocks
31 | - Makes a new zip
32 |
--------------------------------------------------------------------------------
/classes/Hooks.php:
--------------------------------------------------------------------------------
1 | plugin = $plugin;
23 | $this->settingsPage = new SettingsPage($plugin);
24 | }
25 |
26 | /**
27 | * Register all hooks
28 | */
29 | public function addHooks() {
30 | add_action( 'plugins_loaded', [$this->plugin, 'pluginLoaded']);
31 | add_action( 'admin_menu', [$this->settingsPage, 'addPage' ]);
32 | add_action( 'rest_api_init', [$this->plugin->getRestApi(), 'registerRoutes']);
33 | add_action( 'admin_enqueue_scripts', [$this->settingsPage, 'registerAssets']);
34 | }
35 |
36 | /**
37 | * Remove Hooks
38 | */
39 | public function removeHooks() {
40 | remove_action( 'plugins_loaded', [$this->plugin, 'pluginLoaded']);
41 | remove_action( 'admin_menu', [$this->settingsPage, 'addPage' ]);
42 | remove_action( 'rest_api_init', [$this->plugin->getRestApi(), 'registerRoutes']);
43 | remove_action( 'admin_enqueue_scripts', [$this->settingsPage, 'registerAssets'] );
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/classes/Plugin.php:
--------------------------------------------------------------------------------
1 | settings = $settings;
44 | }
45 |
46 | /**
47 | * Initialize the plugin
48 | *
49 | * @since 0.0.1
50 | *
51 | * @uses "ACTION_PREFIX_init" action
52 | *
53 | * @return void
54 | */
55 | public function init(){
56 | if( ! isset($this->api) ){
57 | $this->api = new Api( $this );
58 | }
59 | $this->hooks = new Hooks( $this );
60 | $this->hooks->addHooks();
61 | }
62 |
63 |
64 | /**
65 | * When the plugin is loaded:
66 | * - Load the plugin's text domain.
67 | *
68 | * @uses "plugins_loaded" action
69 | *
70 | */
71 | public function pluginLoaded(){
72 | load_plugin_textdomain( 'pm2-modern-plugin' );
73 | }
74 |
75 | /**
76 | * Get Settings
77 | *
78 | * @since 0.0.1
79 | *
80 | * @return Settings
81 | */
82 | public function getSettings() {
83 | return $this->settings;
84 | }
85 |
86 | /**
87 | * Get API
88 | *
89 | * @since 0.0.1
90 | *
91 | * @return Api
92 | */
93 | public function getRestApi() {
94 | return $this->api;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/classes/Rest/Api.php:
--------------------------------------------------------------------------------
1 | plugin = $plugin;
39 | }
40 |
41 | /**
42 | * Register all routes
43 | *
44 | * @since 1.0.0
45 | *
46 | * @uses "rest_api_init" action
47 | * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
48 | * @return void
49 | */
50 | public function registerRoutes() {
51 | $controller = new SettingsController( $this->plugin );
52 | register_rest_route( $this->namespace, '/settings', [
53 | [
54 | 'methods' => 'GET',
55 | 'callback' => [ $controller, 'get' ],
56 | 'permission_callback' => [ $controller, 'authorize' ],
57 | ],
58 | [
59 | 'methods' => 'POST',
60 | 'callback' => [ $controller, 'update' ],
61 | 'permission_callback' => [ $controller, 'authorize' ],
62 | 'args' => [
63 | 'apiKey' => [
64 | 'required' => true,
65 | 'sanitize_callback' => 'sanitize_text_field',
66 | 'type' => 'string',
67 | ]
68 | ]
69 | ],
70 | ] );
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/classes/Rest/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | plugin = $plugin;
28 | }
29 |
30 | /**
31 | * Default permission_callback
32 | *
33 | * @param \WP_REST_Request $request
34 | * @return bool
35 | */
36 | public function authorize( $request ) {
37 | $capability = is_multisite() ? 'delete_sites' : 'manage_options';
38 | /**
39 | * Filter the capability required to access the endpoint.
40 | *
41 | * @param string $capability
42 | * @param Controller $this
43 | * @param \WP_REST_Request $request
44 | */
45 | $capability = apply_filters( 'ACTION_PREFIX_rest_authorize_capability', $capability, $this, $request );
46 | return current_user_can( $capability );
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/classes/Rest/Controllers/Settings.php:
--------------------------------------------------------------------------------
1 | plugin->getSettings()->getAll();
15 | return [
16 | 'settings' => $settings
17 | ];
18 | }
19 |
20 | /**
21 | * Update settings via API
22 | *
23 | * @param \WP_REST_Request $request
24 | * @return array
25 | */
26 | public function update($request ){
27 | $key = $request->get_param('apiKey');
28 | $this->plugin->getSettings()->save([
29 | 'apiKey' => $key
30 | ]);
31 | $settings = $this->plugin->getSettings()->getAll();
32 | return [
33 | 'settings' => $settings
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/classes/Settings.php:
--------------------------------------------------------------------------------
1 | '',
27 | ];
28 |
29 | /**
30 | * Save settings
31 | *
32 | * @since 0.0.1
33 | *
34 | * @param array $data
35 | * @return void
36 | */
37 | public function save(array $data)
38 | {
39 | $data = array_merge($this->defaults, $data);
40 | /**
41 | * Filter settings before saving
42 | *
43 | * @since 0.0.1
44 | * @param array $data Data to save
45 | */
46 | $data = apply_filters('ACTION_PREFIX_save_settings', $data);
47 | update_option($this->optionName,$data);
48 | }
49 | /**
50 | * Get all settings
51 | *
52 | * @since 0.0.1
53 | *
54 | * @return array
55 | */
56 | public function getAll()
57 | {
58 | $values = get_option($this->optionName, $this->defaults);
59 | /**
60 | * Filter settings before returning
61 | *
62 | * @since 0.0.1
63 | * @param array $values Settings
64 | */
65 | return apply_filters('ACTION_PREFIX_get_settings', $values);
66 | }
67 |
68 | /**
69 | * Get defaults for settings
70 | * @return array
71 | */
72 | public function getDefaults()
73 | {
74 | return $this->defaults;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/classes/SettingsPage.php:
--------------------------------------------------------------------------------
1 | plugin = $plugin;
24 | }
25 |
26 | /**
27 | * Register assets
28 | *
29 | * @since 0.0.1
30 | *
31 | * @uses "admin_enqueue_scripts" action
32 | */
33 | public function registerAssets(){
34 | $dependencies = [];
35 | $version = PM2_MODERN_VERSION;
36 |
37 | // Use asset file if it exists
38 | if ( file_exists( PM2_MODERN_PLUGIN_DIR . 'build/settings.asset.php' ) ) {
39 | $asset_file = include PM2_MODERN_PLUGIN_DIR . 'build/settings.asset.php';
40 | $dependencies = $asset_file['dependencies'];
41 | $version = $asset_file['version'];
42 |
43 | }
44 |
45 | wp_register_script(
46 | SettingsPage::SCREEN,
47 | plugins_url( 'build/settings.js', PM2_MODERN_MAIN_FILE ),
48 | $dependencies,
49 | $version,
50 | );
51 | }
52 | /**
53 | * Adds the settings page to the Settings menu.
54 | *
55 | * @since 0.0.1
56 | *
57 | * @return string
58 | */
59 | public function addPage()
60 | {
61 |
62 | // Add the page
63 | $suffix = add_options_page(
64 | __('PLUGIN_NAME', 'pm2-modern-plugin'),
65 | __('PLUGIN_NAME', 'pm2-modern-plugin'),
66 | 'manage_options',
67 | self::SCREEN,
68 | [
69 | $this,
70 | 'renderPage',
71 | ]
72 | );
73 |
74 | // This adds a link in the plugins list table
75 | add_action(
76 | 'plugin_action_links_' . plugin_basename(PM2_MODERN_MAIN_FILE),
77 | [
78 | $this,
79 | 'addLinks',
80 | ]
81 | );
82 |
83 | return $suffix;
84 | }
85 |
86 | /**
87 | * Adds a link to the setting page to the plugin's entry in the plugins list table.
88 | *
89 | * @since 1.0.0
90 | *
91 | * @param array $links List of plugin action links HTML.
92 | * @return array Modified list of plugin action links HTML.
93 | */
94 | public function addLinks($links)
95 | {
96 | // Add link as the first plugin action link.
97 | $settings_link = sprintf(
98 | '%s ',
99 | esc_url(add_query_arg('page', self::SCREEN, admin_url('options-general.php'))),
100 | esc_html__('Settings', 'pm2-modern-plugin')
101 | );
102 | array_unshift($links, $settings_link);
103 |
104 |
105 | return $links;
106 | }
107 |
108 | /**
109 | * Renders the settings page.
110 | *
111 | * @since 0.0.1
112 | */
113 | public function renderPage()
114 | {
115 | wp_enqueue_script(self::SCREEN);
116 | $settings = $this
117 | ->plugin
118 | ->getSettings()
119 | ->getAll();
120 | wp_localize_script(
121 | self::SCREEN,
122 | 'ACTION_PREFIX',
123 | [
124 | 'apiUrl' => rest_url('pm2-modern-plugin/v1'),
125 | 'settings' => $settings,
126 |
127 | ]
128 | );
129 | ?>
130 |
131 |
132 |
133 |
134 |
135 |
136 | =7.4"
28 | },
29 | "require-dev": {
30 | "automattic/vipwpcs": "^2.3",
31 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2",
32 | "php-coveralls/php-coveralls": "^2.5",
33 | "phpcompatibility/php-compatibility": "10.x-dev as 9.99.99",
34 | "phpcompatibility/phpcompatibility-wp": "dev-master",
35 | "phpunit/phpunit": "^9.5",
36 | "roots/wordpress-core-installer": "^1.100",
37 | "roots/wordpress-full": "^6.0",
38 | "spatie/phpunit-watcher": "^1.23",
39 | "wp-coding-standards/wpcs": "^2.3",
40 | "wp-phpunit/wp-phpunit": "^6.0",
41 | "yoast/phpunit-polyfills": "^1.0"
42 | },
43 | "autoload": {
44 | "psr-4": {
45 | "VendorNamespace\\PluginNamespace\\": "classes/",
46 | "VendorNamespace\\PluginNamespace\\Tests\\": "tests/WordPress/"
47 | }
48 | },
49 | "scripts": {
50 | "lint": "phpcs",
51 | "lint-php8": "phpcs -p --standard=PHPCompatibilityWP --runtime-set testVersion 8.0- --extensions=php --ignore='vendor/,wordpress/,node_modules/' .",
52 | "test": "phpunit",
53 | "test:watch": [
54 | "Composer\\Config::disableProcessTimeout",
55 | "phpunit-watcher watch"
56 | ],
57 | "format": "phpcbf"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/makeZip.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var archiver = require('archiver');
3 | const slug = 'pm2-modern-plugin';
4 | var output = fs.createWriteStream(`${slug}.zip`);
5 | var archive = archiver('zip');
6 |
7 | console.log( 'Zipping!')
8 | output.on('close', function () {
9 | console.log('Zipped!');
10 | console.log(archive.pointer() + ' total bytes');
11 | });
12 |
13 | archive.on('error', function(err){
14 | throw err;
15 | });
16 |
17 | archive.pipe(output);
18 |
19 | archive.append(fs.createReadStream(
20 | __dirname + `/${slug}.php`
21 | ), { name: `/${slug}.php` });
22 |
23 | //Directories to copy
24 | ['classes', 'build', 'vendor/composer'].forEach( ( dir ) => {
25 | archive.directory(dir, '/' + dir);
26 | });
27 |
28 | archive.append(fs.createReadStream(
29 | __dirname + '/vendor/autoload.php'
30 | ), { name: 'vendor/autoload.php' });
31 |
32 | archive.finalize();
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pm2-modern-plugin",
3 | "version": "1.0.0",
4 | "description": "PLUGIN_DESCRIPTION",
5 | "author": "PLUGIN_AUTHOR_NAME",
6 | "license": "GPL-2.0-or-later",
7 | "main": "build/index.js",
8 | "scripts": {
9 | "build": "wp-scripts build",
10 | "lint": "npm-run-all lint:*",
11 | "lint:php": "composer lint",
12 | "lint:css": "wp-scripts lint-style ./src/*.scss ./admin/*.css",
13 | "lint:js": "wp-scripts lint-js ./src",
14 | "format": "npm-run-all format:*",
15 | "format:php": "composer format",
16 | "format:js": "npm run lint:js -- --fix",
17 | "format:css": "npm run lint:css -- --fix",
18 | "packages-update": "wp-scripts packages-update",
19 | "plugin-zip": "wp-scripts plugin-zip",
20 | "start": "wp-scripts start",
21 | "env": "wp-env",
22 | "test:php": "npm run composer test",
23 | "test:watch": "npm run composer test:watch",
24 | "composer": "wp-env run phpunit composer --working-dir=/var/www/html/wp-content/plugins/pm2-modern-plugin",
25 | "preinstall": "composer install",
26 | "prezip:php": "composer install --no-dev -o",
27 | "prezip": "npm-run-all prezip:*",
28 | "prezip:js": "npm run build",
29 | "zip": "npm run prezip && node makeZip.js"
30 | },
31 | "devDependencies": {
32 | "@wordpress/env": "^5.8.0",
33 | "@wordpress/scripts": "^25.0.0"
34 | },
35 | "dependencies": {
36 | "@imaginary-machines/wp-admin-components": "^0.4.1",
37 | "@wordpress/hooks": "^3.23.0",
38 | "archiver": "^5.3.1",
39 | "npm-run-all": "^4.1.5",
40 | "react": "^18.2.0",
41 | "react-dom": "^18.2.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | .
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | tests/*.php
22 | tests/providers/*.php
23 |
24 |
25 | */wordpress/*
26 | */dist/*
27 | */includes/*
28 | */node_modules/*
29 | */vendor/*
30 |
31 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 | tests/WordPress
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/pm2-modern-plugin.php:
--------------------------------------------------------------------------------
1 | init();
49 | }
50 | );
51 | /**
52 | * Start Plugin
53 | *
54 | * @since 1.0.0
55 | * @param Plugin $plugin
56 | */
57 | do_action(
58 | 'ACTION_PREFIX_init',
59 | new Plugin(
60 | new Settings(),
61 | )
62 | );
63 |
--------------------------------------------------------------------------------
/src/api/useSettings.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import apiFetch from '@wordpress/api-fetch';
3 |
4 | //Function for saving settings
5 | const saveSettings = async ( values ) => {
6 | const r = await apiFetch( {
7 | path: '/pm2-modern-plugin/v1/settings',
8 | method: 'POST',
9 | data: values,
10 | } ).then( ( res ) => {
11 | return res;
12 | } );
13 | return { update: r };
14 | };
15 |
16 | const getSettings = async () => {
17 | const r = await apiFetch( {
18 | path: '/pm2-modern-plugin/v1/settings',
19 | method: 'GET',
20 | } ).then( ( res ) => {
21 | return res;
22 | } );
23 | return r;
24 | };
25 |
26 | /**
27 | * Hook for using settings
28 | *
29 | * @return {Object} {saveSettings: function,getSettings:function, isLoaded: boolean, isSaving: boolean, hasSaved: boolean}
30 | */
31 | export const useSettings = () => {
32 | const [ isSaving, setIsSaving ] = React.useState( false );
33 | const [ hasSaved, setHasSaved ] = React.useState( false );
34 | const [ isLoaded, setIsLoaded ] = React.useState( false );
35 | //Reset the isSaving state after 2 seconds
36 | React.useEffect( () => {
37 | if ( hasSaved ) {
38 | const timer = setTimeout( () => {
39 | setIsSaving( false );
40 | }, 2000 );
41 | return () => clearTimeout( timer );
42 | }
43 | }, [ hasSaved ] );
44 | return {
45 | saveSettings: ( values ) => {
46 | setIsSaving( true );
47 | saveSettings( values ).then( () => {
48 | setIsSaving( false );
49 | setHasSaved( true );
50 | } );
51 | },
52 | getSettings: () => {
53 | setIsLoaded( true );
54 | getSettings().then( () => {
55 | setIsLoaded( false );
56 | } );
57 | },
58 | isLoaded,
59 | isSaving,
60 | hasSaved,
61 | };
62 | };
63 | export default useSettings;
64 |
--------------------------------------------------------------------------------
/src/block/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schemas.wp.org/trunk/block.json",
3 | "apiVersion": 2,
4 | "name": "pm2-modern-plugin/block-name",
5 | "version": "0.1.0",
6 | "title": "BLOCK_TITLE",
7 | "category": "text",
8 | "icon": "smiley",
9 | "description": "BLOCK_DESCRIPTION",
10 | "supports": {
11 | "html": false
12 | },
13 | "attributes": {
14 | "content": {
15 | "type": "string",
16 | "source": "html",
17 | "selector": "p"
18 | }
19 | },
20 | "textdomain": "pm2-modern-plugin",
21 | "editorScript": "file:./index.js",
22 | "editorStyle": "file:./index.css",
23 | "style": "file:./style-index.css"
24 | }
25 |
--------------------------------------------------------------------------------
/src/block/edit.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 | import React from 'react';
3 | import { useBlockProps, BlockControls } from '@wordpress/block-editor';
4 | import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
5 |
6 | import './editor.scss';
7 |
8 | export default function Edit( { attributes } ) {
9 | const content = attributes.content || '';
10 | const handler = () => {
11 | // eslint-disable-next-line
12 | alert( 'Hello World!' );
13 | };
14 |
15 | return (
16 |
17 |
18 |
19 |
24 |
25 |
26 | { content }
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/block/editor.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * The following styles get applied inside the editor only.
3 | *
4 | * Replace them with your own styles or remove the file completely.
5 | */
6 |
7 | .wp-block-ufo-ai-wp-ufo-ai-wp {
8 | border: 1px dotted #f00;
9 | }
10 |
--------------------------------------------------------------------------------
/src/block/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
3 | */
4 | import { registerBlockType } from '@wordpress/blocks';
5 |
6 | /**
7 | * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
8 | */
9 | import './style.scss';
10 |
11 | /**
12 | * Internal dependencies
13 | */
14 | import Edit from './edit';
15 | import save from './save';
16 | import metadata from './block.json';
17 |
18 | registerBlockType( metadata.name, {
19 | /**
20 | * @see ./edit.js
21 | */
22 | edit: Edit,
23 |
24 | /**
25 | * @see ./save.js
26 | */
27 | save,
28 | } );
29 |
--------------------------------------------------------------------------------
/src/block/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | * React hook that is used to mark the block wrapper element.
3 | * It provides all the necessary props like the class name.
4 | *
5 | * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
6 | */
7 | import { useBlockProps } from '@wordpress/block-editor';
8 |
9 | /**
10 | * The save function defines the way in which the different attributes should
11 | * be combined into the final markup, which is then serialized by the block
12 | * editor into `post_content`.
13 | *
14 | * @param {Object} root0
15 | * @param {{content:string}} root0.attributes
16 | * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
17 | *
18 | * @return {WPElement} Element to render.
19 | */
20 | export default function save( { attributes } ) {
21 | const content = attributes.content || '';
22 | return { content }
;
23 | }
24 |
--------------------------------------------------------------------------------
/src/block/style.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * The following styles get applied both on the front of your site
3 | * and in the editor.
4 | *
5 | * Replace them with your own styles or remove the file completely.
6 | */
7 |
--------------------------------------------------------------------------------
/src/components/ApiKeyField.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { FieldTr, Input } from '@imaginary-machines/wp-admin-components';
4 | import { Spinner } from '@wordpress/components';
5 |
6 | const name = 'apiKey';
7 | const label = 'Api Key';
8 | const id = 'apiKey';
9 | const ApiKeyField = ( { value, onChange, isSaving } ) => {
10 | return (
11 |
12 |
19 | { ! isSaving ? : null }
20 |
21 | );
22 | };
23 | export default ApiKeyField;
24 |
--------------------------------------------------------------------------------
/src/components/RunOnce.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | const RunOnce = ( { fn } ) => {
3 | useEffect( () => {
4 | fn();
5 | }, [] );
6 | return null;
7 | };
8 | export default RunOnce;
9 |
--------------------------------------------------------------------------------
/src/editor.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * The following styles get applied inside the editor only.
3 | *
4 | * Replace them with your own styles or remove the file completely.
5 | */
6 |
--------------------------------------------------------------------------------
/src/index.php:
--------------------------------------------------------------------------------
1 | ,
11 | id: 'settings',
12 | label: 'Settings',
13 | },
14 | {
15 | children: ,
16 | id: 'otherTab',
17 | label: 'Other',
18 | },
19 | ];
20 |
21 | /**
22 | * Primary App Component
23 | */
24 | const App = () => {
25 | return ;
26 | };
27 |
28 | /**
29 | * Load the settings page
30 | */
31 | render( , document.getElementById( 'pm2-modern-plugin-settings' ) );
32 |
--------------------------------------------------------------------------------
/src/settings/otherTab.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { __ } from '@wordpress/i18n';
3 |
4 | import { Notice, Metabox } from '@imaginary-machines/wp-admin-components';
5 | export default function OtherTab() {
6 | return (
7 |
8 |
15 |
21 |
22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam
23 | sollicitudin tortor lorem, a aliquet elit ultricies eu. In vitae
24 | enim et odio vehicula lacinia ac a tellus. Curabitur sodales,
25 | justo sodales tristique dignissim, nibh diam ultrices leo, ac
26 | vulputate quam felis eget metus. Suspendisse ac mauris sapien.
27 | In a velit finibus, viverra mi eget, lacinia risus. Mauris augue
28 | ex, vulputate vitae iaculis quis, ornare eget nibh. Etiam quis
29 | lacus nec nulla ullamcorper mattis nec nec ligula. Aenean diam
30 | velit, tristique et dolor a, varius convallis nulla. Mauris
31 | imperdiet molestie metus in ornare.
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/settings/settingsTab.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ExternalLink } from '@wordpress/components';
3 | import { __ } from '@wordpress/i18n';
4 |
5 | import {
6 | Form,
7 | FormTable,
8 | TrSubmitButton,
9 | } from '@imaginary-machines/wp-admin-components';
10 |
11 | import ApiKeyField from '../components/ApiKeyField';
12 | import useSettings from '../api/useSettings';
13 |
14 | const SettingsForm = () => {
15 | const { isSaving, saveSettings } = useSettings();
16 | const [ values, setValues ] = React.useState( () => {
17 | //Try to set defaults from localized data
18 | // eslint-disable-next-line no-undef
19 | if ( ACTION_PREFIX.settings ) {
20 | // eslint-disable-next-line no-undef
21 | return ACTION_PREFIX.settings;
22 | }
23 | return {
24 | apiKey: '',
25 | };
26 | } );
27 | const id = 'settings-form';
28 |
29 | //Save settings handler
30 | const onSubmit = ( e ) => {
31 | e.preventDefault();
32 | saveSettings( values ).then( ( { update } ) => {
33 | setValues( { ...values, update } );
34 | } );
35 | };
36 |
37 | return (
38 |
39 |
40 | { __( 'Documentation' ) }
41 |
42 |
61 |
62 | );
63 | };
64 | export default SettingsForm;
65 |
--------------------------------------------------------------------------------
/src/settings/style.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ufo-ai-settings a.components-external-link {
4 | font-size: x-large;
5 | }
6 |
7 | .ufo-ai-wp-wrap h1 {
8 | font-size: 23px;
9 | font-weight: 400;
10 | margin: 0;
11 | padding: 9px 0 4px;
12 | line-height: 1.3;
13 | }
14 |
--------------------------------------------------------------------------------
/tests/WordPress/SettingsTest.php:
--------------------------------------------------------------------------------
1 | save(
19 | ['apiKey' => $value]
20 | );
21 | $this->assertEquals(
22 | ['apiKey' => $value],
23 | get_option('pm2-modern-plugin-settings')
24 | );
25 | }
26 |
27 |
28 | /**
29 | * Can we get the default settings?
30 | * @group settings
31 | */
32 | public function test_get_all_settings() {
33 | $settings = new Settings();
34 |
35 | $current = $settings->getAll();
36 | $this->assertEquals(
37 | $settings->getDefaults(),
38 | $current,
39 | );
40 | $value = 'changed';
41 | $settings->save(
42 | ['apiKey' => $value]
43 | );
44 | $this->assertEquals(
45 | ['apiKey' => $value],
46 | $settings->getAll()
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/WordPress/SetupTest.php:
--------------------------------------------------------------------------------
1 | assertTrue( defined( 'PM2_MODERN_PLUGIN_DIR' ) );
21 | $this->assertTrue( defined( 'PM2_MODERN_VERSION' ) );
22 | $this->assertTrue( defined( 'PM2_MODERN_MAIN_FILE' ) );
23 | }
24 |
25 | /**
26 | * Classes loaded?
27 | * - autoloader works
28 | * - files exist
29 | *
30 | * @group setup
31 | */
32 | public function test_classes_exist() {
33 |
34 | $this->assertTrue( class_exists( Plugin::class ) );
35 | }
36 |
37 | /**
38 | * Test adding hooks.
39 | *
40 | * @group setup
41 | * @group hooks
42 | */
43 | public function test_add_hooks() {
44 | $plugin = $this->makePlugin();
45 | $plugin->init();
46 | $this->assertGreaterThan(
47 | 0,
48 | has_action(
49 | 'plugins_loaded',
50 | [$plugin,'pluginLoaded']
51 | )
52 | );
53 |
54 | $this->assertGreaterThan(
55 | 0,
56 | has_action(
57 | 'rest_api_init',
58 | [$plugin->getRestApi(),'registerRoutes']
59 | )
60 | );
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/WordPress/TestCase.php:
--------------------------------------------------------------------------------
1 |