wordpress

WordPress Plug-in – Kontaktformulär.

Alla sidor behöver ett kontaktformulär. Sedan jag började jobba med WordPress för några år sedan har jag försökt att förlita mig så lite som möjligt på externa plug-ins. Inte för att jag tycker illa om Plug-ins generellt utan för att jag gillar att veta hur saker fungerar. Jag vill bygga mina egna “grejer” helt enkelt. I den här artikeln tänkte jag dela med mig lite av vad jag har lärt mig genom åren, jag tänkte att något enkelt som ett kontaktformulär kunde vara ett bra exempel.

Det här kommer vi gå igenom i den här artikeln.

  • Skapa en Block-plug-in
  • Skapa en egen post type i WordPress
  • Verifiera formuläret med Google reCaptcha
  • Skicka mail med PHP
  • Spara meddelande i WordPress databas som en egen post type

Innan vi börjar vill jag också säga att jag kommer använda två olika verktyg i mina exempel. VS Code och Local by Flywheel. Båda verktygen är gratis men är i min åsikt ovärderliga. Local är inte bara en lokal webbserver utan gör det också lätt att installera WordPress lokalt. Du kan även skapa en lösenordsskyddad extern länk så att andra kan titta på sidan vart som helst ifrån. Med den hyllningen skulle man kunna tro att jag är sponsrad men man får ju ingen “spons” när ingen vet vem man är.

Vill du veta hur du registrerar din sida för reCAPTCHA hos Google kan du läsa lite mer om det här.

En ytterligare liten detalj är att jag kommer använda det förinstallerade temat “Twenty Twenty-Two”. Det kommer påverka hur formuläret ser ut.

GDPR

Google reCAPTCHA följer inte GDPR. Viktigt att påpeka är att genom att följa exemplen i den här artikeln skapar vi en plug-in som inte följer rådande riktlinjer för GDPR. Använder du Google reCAPTCHA och samtidigt vill följa GDPR måste du informera användaren. Det du måste informera om är att Google kommer samla in data om användaren. Den informationen kommer skickas till USA.

Skapa en Block-plug-in

Det första steget här är väldigt enkelt. Allt man behöver göra är att skriva en rad kod i Terminalen… förutsatt att du har installerat node.js förstås (installera Node.js). Det behöver man först och främst för “npm” men också för att huvuddelen av utvecklingen av WordPress-Block sker med hjälp av React. Eller rättare sagt, WordPress har lagt ett ytterligare abstraktionslager över React och det är det vi kommer använda. Vi kommer även använda oss utav JSX. För att kunna följa den här artikeln behöver du verkligen inte vara en stjärna på varesig React eller JSX. Men grunderna är bra om har.

Känner du inte till “npm” så står det för “Node Package Manager”. Väldigt förenklat kan man säga att npm är en enorm samling av lösningar på utmaningar du stöter på som Javascript-utvecklare. Man “kopplar” på ett paket som löser problemen man står inför. Läs mer om NPM här.

Är du inte van vid att använda Terminalen och helst inte “cd:ar” dig dit du vill? Låte VS Code hjälpa dig. Gå till File->Open Folder…, välj plugins-mappen för den lokala WordPress-installation som du vill jobba med.

Väl där skriver du följande i Terminalen (alltså det efter %).

~ plugins % npx @wordpress/create-block Kontaktblock

Så, nu har du gjort en plug-in för WordPress. Den är såklart helt oanvändbar och ser fruktansvärd ut rent visuellt men det är en plugin. Nu behöver du, via terminalen, gå in i den nyss skapade mappen för din plug-in.

~ plugins % cd kontaktblock

Väl där kör du:

~ kontaktblock % npm run start

Nu har du kompilerat alla filer till en fungerande plug-in som du kan aktivera i WordPress, så gör det nu. Aktivera den alltså. Du har också startat en “vakt” som kommer kompilera om din kod när du har gjort några ändringar. Den kommer också “skrika” när du har gjort fel. Jättepraktiskt.

Innehållet i mappen

Du borde nu ha en mapp med det här innehållet.

  • build – mapp
  • kontaktblock.php
  • node_modules – mapp
  • package-lock.json
  • package.json
  • readme.txt
  • src – mapp

Börja med att lägga till en sak i kontaktblock.php. Det handlar om att säkerställa att ingen lyckas använda filen “kontactblock.php” direkt. Utan att gå via WordPress där din plug-in är installerad.

Så direkt efter kommentarerna, före funktionen “create_block_kontaktblock_block_init”, lägger du in följande:

if ( ! defined( 'ABSPATH' ) ) {
	die('Stopp där! Så får man inte göra!');
}

Om du tycker att meddelandet i die-funktionen är fånigt kan du ta bort det eller skriva vad du vill. Vi kommer återkomma till den här filen senare. Men för nu är vi klara med den.

Eftersom jag inte tänkte att man skulle kunna ändra i vårt kontaktformulär i WordPress. Då menar jag lägga till eller ta bort fält i formuläret. Därför behöver vi inte göra så mycket i edit.js-funktionen. Det räcker med att gör ett “placeholder”-block. Så i src-mappen öppnar du edit.js, innehållet borde se ut som nedan.

Kontaktformuläret i WordPress “backend”

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */
import { useBlockProps } from '@wordpress/block-editor';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {WPElement} Element to render.
 */
export default function Edit() {
	return (
		<p { ...useBlockProps() }>
			{ __( 'Kontaktblock – hello from the editor!', 'kontaktblock' ) }
		</p>
	);
}

Här i behöver vi göra två saker. Som “input” i useBlockProps-functionen på rad 34 lägger vi till följande:

{className: "kf-placeholder"}

I JSX använder man className istället class eftersom class är en reserverar benämning. Klammerparenteserna visar i det här fallet att “className” är del av ett “object”. Klammerparenteserna runt “…useBlockProps()” talar om för JSX att här vill vi använda “normal” JavaScript-syntax.

Efter det lägger vi in en ny text i stället för ‘Kontaktblock – hello from the editor!’ på rad 35. Skriv något i stil med ‘Här kommer det ligga ett kontaktformulär’. Eller skriv något kul vetja. Man skulle också kunna ta bort hela internationaliserings-funktionen och ersätta med en text om man vill. Om man, hur otroligt det än kan vara, framöver kommer vilja översätta texten i sin plug-in kommer man behöva den.

Din “Edit-funktion” borde se ut ungefär såhär:

export default function Edit() {
	return (
		<p { ...useBlockProps( { className: 'kf-placeholder' } ) }>
			{ __( 'Här kommer det ligga ett kontaktformulär', 'kontaktblock' ) }
		</p>
	);
}

Dags att aktivera

Nu kan det vara läge att aktivera sin plug-in i WordPress och se vad vi har. Om allt har fungerat som det ska borde du nu ha ett nytt Block som heter “Kontaktblock”. Det ska ligga under kategorin “Widgets” och ha en “smiley” som ikon. Om man lägger in det på en sida eller i ett inlägg borde det se ut ungefär såhär:

Inte speciellt snyggt och inget kontaktformulär men eftersom det är en “placeholder” spelar det ju inte så stor roll. Däremot kan man “snygga till” det med lite CSS. Öppna editor.scss i src-mappen. Ta bort den CSS-regel som ligger där och ersätt med nedan:

 .kf-placeholder {
	background-color: lightgrey;
	font-size: 2rem;
	text-align: center;
	padding: 2rem;
}

När du har uppdaterat sidan borde du ha något som ser ut som nedan:

Inte heller snyggt men tydligt för användaren och det gör i alla fall inte ont i ögonen när man tittar på det.

Kontaktformulärets innehåll

Nu tycker jag att vi gör något mycket roligare. Nu är det nämligen dags att skapa det som kommer synas på “framsidan”, det gör man i save.js-filen som även den finns i src-mappen. Här lägger vi till HTML för att skapa ett kontaktformulär, helt korrekt vore att säga att vi lägger till JSX. Eftersom det tekniskt sett är JavaScript vi skriver, men formaterat som HTML. Hur som helst tänker jag att save-funktionen ska se ut ungefär som nedan:

export default function save() {
	return (
		<div {...useBlockProps.save({ className: "kf_container" })}>
		  <form method="POST">
		    <input type="text" name="namn" placeholder="Ditt namn..." />
		    <input type="email" name="email" placeholder="Din email..." />
		    <textarea
			name="meddelande"
			placeholder="Ditt meddelande..."
			rows="8"
		    ></textarea>
		    <input type="submit" name="skicka" value="Skicka">
		  </form>
		</div>
	);
}

Det vi har gjort här är en container-div för innehållet, ett formulär med “metod = POST” och textfält för namn, email och meddelande. Samt en knapp för att skicka.

Spar och uppdaterar sidan (se till att kör “npm run start” så att koden kompileras). När du tittar på sidan ser du ett kontaktformulär som antagligen ser ut ungefär så här.

Fullkomligt gräsligt. Eftersom den här artikeln inte handlar om CSS kommer jag enbart göra en enkel layout, jag kommer inte göra allt enligt “best practice”. Utan på ett enkelt och tydligt sätt. Därför lägger vi till följande CSS i filen style.scss som ligger i src-mappen.

Designen…

.kf_container {
	width: 100%;
	max-width: 10em;
}

.kf_container form {
	display: flex;
	flex-direction: column;
	gap: .25em;
	width: 100%;
}

.kf_container form input,
.kf_container form textarea {
	font-family: inherit;
	font-size: 1rem;
	padding: .5rem 1rem;
	border: 1px solid black;
}

.kf_container form input[type="submit"] {
	display: inline-block;
	border: none;
	border-radius: 0;
	width: fit-content;
	margin-left: 0;
	font-size: 18px;
	padding: calc(.667em + 2px) calc(1.333em + 2px);
}

Då kommer kontaktformuläret se ut ungefär som nedan:

Inget mästerverk men klart mycket bättre. Jag bör väll också påpeka att vårat kontaktformulär nu är “stylat” för att passa just det här temat (Twenty Twenty-Two). Undertiden jag skrev den här artikeln testade jag formuläret i Astra-temat. Det såg ganska bra ut, utan att jag hade skrivit en enda rad CSS. Ett tips är att skriva sin CSS så universell som möjligt. Testa det med flera olika teman. Så, nog med CSS för den här artikeln handlar inte om visuell design.

Custom Post Type

Nu har vi i alla fall ett kontaktformulär som funkar visuellt. Men det gör ju ingenting, inget händer när du trycker på “Skicka”, det fixar vi om en liten stund. Först måste vi ha någon plats att lagra informationen. Det kan vi göra i en “custom post type”, då blir det enkelt att överblicka. Det får dessutom sin egen meny i WordPress back-end.

Öppna “kontaktblock.php” och lägger till följande kod. Lägg koden efter vår koll av vem som får använda filen och innan vi registrera vårat block. Ordningen är egentligen inte viktig utan handlar mer om hur jag personligen gillar att lägga upp det.

function kf_message_post_types() {
	register_post_type('kf_message', [
		"public" => true,
		'show_in_rest' => true,
		"labels" => [
			"name" => "Meddelanden",
			"add_new_item" => "Lägg till nytt Meddelande",
			"all_items" => "Alla Meddelanden"
		],
		"menu_icon" => "dashicons-email"
	]);
}

add_action('init', 'kf_message_post_types');

När du har sparat och uppdaterat din WordPress Back-end-sida så borde du se en ny meny i det vänstra verktygsfältet som heter “Meddelanden”. Menyn har en brev-symbol framför, lysande!

Formatera för WordPress

Nu är det dags att börja ta emot informationen från kontaktformuläret. Vi börjar med att kolla om man har tryckt på “Skicka-knappen”, det gör vi med isset-funktionen. Fortfarande i “kontaktblock.php” lägger vi till följande kod:

function insert_message_post() {
	if (isset($_POST['skicka'])) {
		$kf_name      = sanitize_text_field( $_POST['namn'] );
		$kf_email     = sanitize_email( $_POST['email'] );
		$kf_message   = sanitize_text_field( $_POST['meddelande'] );
	
		$kf_post_content = "Namn: {$kf_name}\nEmail: {$kf_email}\nMeddelande: {$kf_message}";

		$contact_post = [
			"post_type" => "kf_message",
			"post_title" => $kf_name,
			"post_content" => $kf_post_content,
			"post_status" => "draft"
		];
	
		echo "<pre>";
		print_r($contact_post);
		echo "</pre>";
	}
}

add_action('init', 'insert_message_post');

I koden ovanför skapar vi en ny funktion som vi låter WordPress köra tillsammans med “init”. På så sätt säkerställer vi att WordPress har kört alla nödvändiga funktioner innan vi kör vår. När det är klart säger vi att om man trycker på knappen med namnet “skicka” ska vi plocka in värdena från fälten och lägga dessa i variablerna $kf_name, $kf_email och $kf_message.

Efter det skapar vi ytterligare en ny variabel, $kf_post_content, där vi lägger in värdena från $kf_name, $kf_email och $kf_message. Sedan slår vi ihop det till en Array med nycklarna “post_type”, “post_title”, “post_content” och “post_status” i variabeln $contact_post. Vi skapa även ytterligare en variabel med namnet $kf_post_content. Den kommer vi använda senare för att lägga in i meddelandet som sparas i vår “custom post type”. Anledningen till att vi slår ihop $contact_post i en Array formaterad på det här sättet är för att WordPress vill ha den strukturen när man sparar till databasen. Vilket vi kommer göra lite senare.

Om du går till sidan som du har lagt in ditt kontaktformulär-block på och fyller i det borde du få upp någonting som liknar det i bilden bredvid när du trycker på skicka:

Jag gissar att själva meddelandet du skrev inte handlade om en apa i bur men det är okej. Du är förlåten.

Validering av vårat kontaktformulär

Eftersom allt det här fungerade kan det vara läge att ta hand om valideringen av kontaktformuläret. Som vanligt inom programmering finns det massor av sätt att lösa det på. Jag tänker att det enklaste sättet är att göra det på är med hjälp av JavaScript.

För att det ska fungera måste vi registrera en JavaScript-fil, det gör vi i “kontaktblock.php” och det går till såhär:

function kf_enqueue_scripts() {
	wp_enqueue_script( 'kf_validate', plugin_dir_url(__FILE__) . 'js/kf_validate.js');
}
add_action( 'enqueue_block_assets', 'kf_enqueue_scripts');

Nu måste vi såklart också skapa mappen som scriptet ska ligga i och själva scriptet. I plug-in-mappen skapar vi en ny mapp, kalla den “js” kort och gott. Skapa scriptfilen i den nya mappen och döp den till “kf_validate.js”.

I den nya filen lägger vi in följande kod:

function validateForm() {
	let namn = document.forms["kf_form"]["namn"].value;
	let email = document.forms["kf_form"]["email"].value;
	let meddelande = document.forms["kf_form"]["meddelande"].value;
	if (namn == "" || email == "" || meddelande == "") {
		alert("Du måste fylla i alla fält.");
		return false;
	}
}

Så, nu kan ingen skicka utan att ha fyllt i alla fält i vårt kontaktformulär. Det går såklart att göra en tjusigare validering där man även kollar att formateringen av email-adressen är “sannolik”. Och att man med CSS-regler visar vilket fält som är fel ifyllt eller tomt. Men den här artikeln är lång nog ändå.

Validering av kontaktformulär

För att få kontaktformuläret att “köra” vårat nya script måste vi ändra lite i “save.js”. Först måste vi ge form-taggen ett namn. Vi måste också berätta för vårt kontaktformulär att vi vill köra ett script när någon trycker på “skicka”. Det gör vi genom att lägga till en “onsubmit” med en hänvisning till vår nya fina funktion. Efter att detta är klart bör koden se ut ungefär såhär:

export default function save() {
	return (
		<div {...useBlockProps.save({ className: "kf_container" })}>
			<form name="kf_form" method="POST" onsubmit="return validateForm()">
				<input type="text" name="namn" placeholder="Ditt namn..." />
				<input type="email" name="email" placeholder="Din email..." />
				<textarea
					name="meddelande"
					placeholder="Ditt meddelande..."
					rows="8"
				></textarea>
				<button name="skicka" className="wp-block-button__link">
					Skicka
				</button>
			</form>
		</div>
	);
}

Det vi har gjort är att vi har lagt till name=”kf_form” och onsubmit=”return validateForm()” i form-taggen.

Ta hand om informationen

Vi har kommit ganska långt men har fortfarande en hel del kvar att göra. Nu är det dags att vi spar informationen från formuläret till WordPress databas. Det gör vi med hjälp av WordPress inbyggda funktion “wp_insert_post()”. Eftersom vi har gjort så mycket förberedande kod tidigare är “wp_insert_post()” det ända vi behöver lägga till. Så istället för “pre-taggarna” och allt emellan dessa i vår “insert_message_post”-funktion i kontaktblock.php lägger vi till följande:

wp_insert_post($contact_post);

När du testar kontakformuläret nu och går in i den nya menyn i WordPress, Meddelanden, som vi skapade tidigare. Borde du se en ett nytt inlägg. Om du klickar på det borde du komma till en sida där du se det du fyllde i formuläret med. Ganska coolt tycker jag.

Skicka mail med WordPress

Nu går vi vidare med att skicka ett mail samtidigt. Tyvärr är det här en funktion som vi inte kommer kunna testa utan att ha en mail-server installerad lokalt. Därför måste vi lita på att den fungerar tills vi kan aktivera vår plug-in på en sida som ligger “live”.

Förutom att vi inte kommer kunna testa den är även detta enbart en rad kod. Så enkelt har WordPress gjort det för oss. Vi lägger helt enkelt bara till kodraden nedanför, efter “wp_insert_post”-funktionen. Byt ut ### din mailadress ### mot den mailadress du vill skicka meddelanden till.

wp_mail('### din mailadress ###', $kf_name, $kf_post_content);

Nu har vi faktiskt en fullt fungerade plug-in som spara våra användares meddelande in i WordPress databas. Och samtidigt skickar ett mail till oss. Tyvärr är det helt öppet för robotar att fylla i och använda. Det betyder att vi skulle få enorma mängder spam och försök till email-kapning.

Stoppa spam

För att slippa det måste vi lägga in ett ytterligare lager av skydd. Det kommer vi göra med hjälp av Google reCAPTCHA.

Om det här ska fungera krävs det att du har en “Google reCAPTCHA-egendom” registrerad. Du måste ha en “site key”, en “secret key” och din utvecklardomän bland dina registrerade domäner.

Saknar du det kan du läsa mer om hur man skapar sin Google reCAPTCHA-egendom här.

Vi börjar med att registrera länken till API:t i “kontaktblock.php”, efter att vårt block registreras lägger vi till följande:

function register_recaptcha_script() {
	wp_enqueue_script('recaptcha', "https://www.google.com/recaptcha/api.js");
}
add_action( 'init', 'register_recaptcha_script');

I “save.js”, innan “skicka-knappen” lägger vi till koden nedanför. Här behöver du lägga in din personliga “site key” så byt ut ### din site key ### mot den.

<div className="g-recaptcha" data-sitekey="### din site key ###" ></div>

Lägg krokben för robotarna

Nu kommer vi till det som verkligen kommer sätta stopp för robotarna. För nu ska vi skicka data till Google som kommer avgöra om det är en robot eller inte.

För det behöver vi vår hemliga nyckel, informationen från reCAPTCHA i kontaktformuläret och vår användares IP-adress. Nu tänker du kanske, är det här ok ur ett GDPR-perspektiv? Nej, det är det faktiskt inte. Att behandla en användares IP-adress utan ett medgivande är inte ok enligt GDPR. Att informationen dessutom behandlas utanför EU gör det inte heller bättre. I den här artikel kommer vi dock inte ta hänsyn till detta utan det. Det är upp till dig att meddela dina användare om detta.

Tänk på det här med mail

Det finns lite olika saker att tänka på när det gäller att skicka email från en webbsida. Framför allt är det superviktigt att robotar inte kan använda ditt kontaktformulär för att skicka skräppost. Om man möjliggör detta kommer man ganska snabbt upptäcka att man har en “svartlistad” domän. Alltså en domän som spärras i filter för skräppost. Det är speciellt viktigt om man tänker att man ska skicka en “auto reply” till den som har fyllt i formuläret. Anledningen till det är att robotar använder den funktionen för att få det att se ut som att det är du som svarar på ett mail. Läs mer om det här. Det är alltså inte du som är målet utan den påstådda avsändaren. Däremot är det du som kommer få “skulden”, och det vill vi ju inte. Av den anledningen kommer vi inte lägga in någon autoreply.

Nu är det i alla fall dags att slutföra vår plug-in. Öppna kontaktblock.php och lägg in följande kod efter $contact_post i insert_message-funktionen.

$secret_key = "### din secret key ###";
$response_key = $_POST['g-recaptcha-response'];
$user_IP = $_SERVER['REMOTE_ADDR'];
$url = "https://www.google.com/recaptcha/api/siteverify?secret=$secret_key&response=$response_key&remoteip=$user_IP";
	
$response = file_get_contents($url);
$response = json_decode($response, true);

I koden ovanför skapar vi sex nya variabler. $secretKey, $responseKey, $userIP, $url och $respense. I $secretKey lägger du in din, hör och häpna, secret key, som du fick av Google. Efter det tar vi hand om det som kommer från reCAPTCHA-boxen i $responseKey och även användarens IP-adress. Allt det här lägger vi ihop i en URL som vi senare använder för att få Googles värdering av användaren. Det gör vi med hjälp av PHP-funktionen file_get_contents där vi lägger in $url som input.

Vår respons

Vi återanvänder $response för den avkodade json-responsen vi får från Google. Apropå det, nu är det dags att vi använder det vi har fått från Google. Det gör vi med hjälp av följande kodsnutt.

if ($response["success"] === true) {
	wp_insert_post($contact_post);
	wp_mail('info@pivemo.se', $kf_name, $kf_post_content);
} else {
	echo "Det fungerade inte så bra, är du en robot?";
}

Här kollar vi att “success” är “true”, om inte lägger vi upp ett meddelande. Felmeddelandet bör göras snyggare och även som en popup som går att stänga. Men det är utanför poängen med den här artikeln.

Klar!

Om allt funkar som det ska, det gjorde det för mig när jag testade den, så är vår plug-in klar. I alla fall så långt jag tänkte dra ut den här artikeln.

Jag uppmanar alla som använder den här artikeln för att skapa en plug-in som man tänker använda att förbättra säkerheten, valideringen och felmeddelandet. Men framförallt att upplysa användaren om att genom att använda sidan kommer Google spionera på dig. Och spara din data och troligtvis även sälja den vidare.

Ville du bara ha koden?

Om det är så att du inte orkade läsa allt och bara vill ha koden. Eller om du vill se hur den slutgiltiga koden i de olika filerna ser ut följer dessa här. Värt att notera är att jag har valt att lämna alla kommentarer från de olika filerna som skapades med npx @wordpress/create-block. Alla dessa kommentarer kan man ta bort, med undantag för de inledande kommentarerna i kontaktblock.php. Dessa är nödvändiga för att din plug-in ska kunna registreras och aktiveras i WordPress.

Om det är så att du vill använda koden till en “riktig” plug-in rekommenderar jag att kompilera koden med npm run build. Då kommer alla icke nödvändiga kommentarer tas bort.

kontaktblock.php

<?php
/**
 * Plugin Name:       Kontaktblock
 * Description:       Example block scaffolded with Create Block tool.
 * Requires at least: 5.9
 * Requires PHP:      7.0
 * Version:           0.1.0
 * Author:            The WordPress Contributors
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       kontaktblock
 *
 * @package           create-block
 */

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */

if ( ! defined( 'ABSPATH' ) ) {
	die('Stopp där! Så får man inte göra!');
}

// OUR CUSTOM POST TYPE 
function kf_message_post_types() {
	register_post_type('kf_message', [
		'show_in_rest' => true,
		'supports' => ['title', 'editor'],
		"public" => true,
		"labels" => [
			"name" => "Meddelanden",
			"add_new_item" => "Lägg till nytt Meddelande",
			"edit_item" => "Ändra offert",
			"all_items" => "Alla Meddelanden",
			'singular_name' => 'Meddelande'
		],
		"menu_icon" => "dashicons-email"
	]);
}

add_action('init', 'kf_message_post_types');

// REGISTER BLOCK TYPE
function create_block_kontaktblock_block_init() {
	register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'create_block_kontaktblock_block_init' );

// REGISTER RECAPTCHA SCRIPT
function register_recaptcha_script() {
	wp_enqueue_script('recaptcha', "https://www.google.com/recaptcha/api.js");
}
add_action( 'init', 'register_recaptcha_script');


// REGISTER VALIDATION SCRIPT
function kf_enqueue_scripts() {
	wp_enqueue_script( 'kf_validate', plugin_dir_url(__FILE__) . 'js/kf_validate.js');
}
add_action( 'enqueue_block_assets', 'kf_enqueue_scripts');

// HANDLE INFO FROM FORM
function insert_message_post() {
	if (isset($_POST['skicka'])) {
		$kf_name      = sanitize_text_field( $_POST['namn'] );
		$kf_email     = sanitize_email( $_POST['email'] );
		$kf_message   = sanitize_text_field( $_POST['meddelande'] );
	
		$kf_post_content = "Namn: {$kf_name}\nEmail: {$kf_email}\nMeddelande: {$kf_message}";
	
		$contact_post = [
			"post_type" => "kf_message",
			"post_title" => $kf_name,
			"post_content" => $kf_post_content,
			"post_status" => "draft"
		];
	
		// reCaptcha validation
		$secret_key = "### din secret key ###";
		$response_key = $_POST['g-recaptcha-response'];
		$user_IP = $_SERVER['REMOTE_ADDR'];
		$url = "https://www.google.com/recaptcha/api/siteverify?secret=$secret_key&response=$response_key&remoteip=$user_IP";
	
		$response = file_get_contents($url);
		$response = json_decode($response, true);
	
		if ($response["success"] === true) {
			wp_insert_post($contact_post);
			wp_mail('info@pivemo.se', $kf_name, $kf_post_content);

		} else {
			echo "Det fungerade inte så bra, är du en robot?";
		}
	
	}
}

add_action('init', 'insert_message_post');

edit.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from "@wordpress/i18n";

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */
import { useBlockProps } from "@wordpress/block-editor";

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import "./editor.scss";

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {WPElement} Element to render.
 */
export default function Edit() {
	return (
		<p {...useBlockProps({ className: "kf-placeholder" })}>
			{__("Här kommer det ligga ett kontaktformulär", "kontaktblock")}
		</p>
	);
}

save.js

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */
import { useBlockProps } from "@wordpress/block-editor";

/**
 * The save function defines the way in which the different attributes should
 * be combined into the final markup, which is then serialized by the block
 * editor into `post_content`.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
 *
 * @return {WPElement} Element to render.
 */
export default function save() {
	return (
		<div {...useBlockProps.save({ className: "kf_container" })}>
			<form name="kf_form" method="POST" onsubmit="return validateForm()">
				<input type="text" name="namn" placeholder="Ditt namn..." />
				<input type="email" name="email" placeholder="Din email..." />
				<textarea
					name="meddelande"
					placeholder="Ditt meddelande..."
					rows="8"
				></textarea>
				<div
					className="g-recaptcha"
					data-sitekey="### din site key ###"
				></div>
				<input
					type="submit"
					name="skicka"
					className="wp-block-button__link"
					value="Skicka"
				/>
			</form>
		</div>
	);
}

editor.scss

/**
 * The following styles get applied inside the editor only.
 *
 * Replace them with your own styles or remove the file completely.
 */

 .kf-placeholder {
	background-color: lightgray;
	font-size: 2rem;
	text-align: center;
	padding: 2rem;
}

.kf_container {
	width: 100%;
	max-width: 10em;
}

.kf_container * {
	font-family: inherit;
}

style.scss

/**
 * The following styles get applied both on the front of your site
 * and in the editor.
 *
 * Replace them with your own styles or remove the file completely.
 */

.kf_container {
	width: 100%;
	max-width: 10em;
}

.kf_container form {
	display: flex;
	flex-direction: column;
	gap: .25em;
	width: 100%;
}

.kf_container form input,
.kf_container form textarea {
	font-family: inherit;
	font-size: 1rem;
	padding: .5rem 1rem;
	border: 1px solid black;
}

.kf_container form input[type="submit"] {
	display: inline-block;
	border: none;
	width: fit-content;
	margin-left: 0;
	font-size: 18px;
	padding: calc(.667em + 2px) calc(1.333em + 2px);
}

kf_validate.js

function validateForm() {
	let namn = document.forms["kf_form"]["namn"].value;
	let email = document.forms["kf_form"]["email"].value;
	let meddelande = document.forms["kf_form"]["meddelande"].value;
	if (namn == "" || email == "" || meddelande == "") {
		alert("Du måste fylla i alla fält.");
		return false;
	}
}

Disclamer

När och om du använder koden i artikeln bär du själv fullt ansvar för den. Jag tar inget som helst ansvar för eventuella säkerhetshål som kan uppkomma i samband med att använda koden från denna artikel. Genom att använda “produkten” av koden i artikeln skickar du användarens IP-adress och annan känslig information till land utanför EU. Vilket är ett brott mot GDPR om inte användaren får tillfälle att neka till detta. Artikeln nämner även andra publika företag, jag/Pivemo har inga som helst samarbeten med någon av dessa företag.