Skip to content

Commit

Permalink
feature: Add Form Extension (#4)
Browse files Browse the repository at this point in the history
* chore: Update composer

* feature: Add form extension

* Refactor: ActiveCampaign has block requirements (#5)

* feature: add block placeholder and notice

* feature: add window declaration

* refactor: remove inline window declaration, update list and tag options, use new placeholder and notice

* refactor: use credential test prior to api calls, add method for tags, update localized script.

* feature: add credential test prior to adding new block

* refactor: replace blocknotice

* refactor; reformat, update strings.

* refactor: reformat remove spaces in src/FormExtension/Actions/EnqueueFormBuilderScripts.php

* doc: update unreleased tags

* doc: update unreleased tags

* refactor: replace relative url path with admin_url for better WP support

* Refactor: block design improvements (#6)

* feature: add block placeholder and notice

* feature: add window declaration

* refactor: remove inline window declaration, update list and tag options, use new placeholder and notice

* refactor: use credential test prior to api calls, add method for tags, update localized script.

* feature: add credential test prior to adding new block

* refactor: replace blocknotice

* refactor; reformat, update strings.

* refactor: reformat remove spaces in src/FormExtension/Actions/EnqueueFormBuilderScripts.php

* doc: update unreleased tags

* doc: update unreleased tags

* refactor: replace relative url path with admin_url for better WP support

* refactor: update template styles for consistency

* refactor: reformat

* refactor: update & export window types

* feature: add tag & list controls. Split InspectorControls into new directory

* refactor: use update inspectorControls

* chore: remove debug log

* chore: formatting

* refactor: add ending bracket

* refactor: update metadata default label

* refactor: update template defaultChecked prop to checkeda

* refactor: replace selectedEmailList attribute to selectedLists

* refactor: update list control label

* refactor: fix translation domain on subscriber tags

* refactor: update listControl to always have one list selected

* refactor: use array fallback on selectedLists

* refactor: update domain names

* refactor: return early on methods requiring credential tests

* Refactor: use early return on add block to new form

* refactor: fix double negative conditonal

* refactor: update conditionals

* refactor: enqueue scripts in header

---------

Co-authored-by: Joshua Dinh <[email protected]>
Co-authored-by: Joshua Dinh <[email protected]>
3 people authored Apr 25, 2024
1 parent 46412dd commit 3be6c99
Showing 29 changed files with 44,497 additions and 1,798 deletions.
26 changes: 17 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
{
"name": "impress-org/give-activecampaign",
"authors": [
{
"name": "GiveWP",
"email": "[email protected]",
"homepage": "https://givewp.com",
"role": "Developer"
}
],
"type": "wordpress-plugin",
"scripts": {
"unreleased": "./vendor/bin/since-unreleased.sh"
},
"require": {
"activecampaign/api-php": "2.0.3"
},
"minimum-stability": "dev"
"require-dev": {
"kjohnson/since-unreleased": "^1.0"
},
"autoload": {
"psr-4": {
"GiveActiveCampaign\\": "src/"
}
},
"config": {
"platform": {
"php": "7.2"
}
}
}
32 changes: 30 additions & 2 deletions give-activecampaign.php
Original file line number Diff line number Diff line change
@@ -64,6 +64,14 @@ class Give_ActiveCampaign {
*/
public $notices = array();

/**
* @unreleased
* @var array
*/
private $serviceProviders = [
\GiveActiveCampaign\FormExtension\ServiceProvider::class,
];

/**
* Returns the singleton instance of this class.
*
@@ -87,7 +95,12 @@ public static function get_instance() {
*/
private function setup() {

add_action( 'give_init', array( $this, 'init' ), 10 );
require_once GIVE_ACTIVECAMPAIGN_PATH . '/vendor/autoload.php';

// Load service providers.
add_action('before_give_init', [$this, 'registerServiceProviders']);

add_action( 'give_init', array( $this, 'init' ), 10 );
add_action( 'admin_init', array( $this, 'check_environment' ) );
add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
add_action( 'give_add_email_tags', array( $this, 'add_email_tags' ), 9999999 );
@@ -99,6 +112,22 @@ private function setup() {

}

/**
* Register service providers
*
* @unreleased
*/
public function registerServiceProviders()
{
if ( ! $this->get_environment_warning() ) {
return;
}

foreach ($this->serviceProviders as $className) {
give()->registerServiceProvider($className);
}
}

/**
* Init the plugin after plugins_loaded so environment variables are set.
*
@@ -120,7 +149,6 @@ public function init() {
return false;
}

require_once GIVE_ACTIVECAMPAIGN_PATH . '/vendor/autoload.php';
require_once GIVE_ACTIVECAMPAIGN_PATH . '/includes/helpers.php';
require_once GIVE_ACTIVECAMPAIGN_PATH . '/includes/metabox.php';

60 changes: 0 additions & 60 deletions gulpfile.js

This file was deleted.

45,183 changes: 43,470 additions & 1,713 deletions package-lock.json

Large diffs are not rendered by default.

39 changes: 25 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
{
"name": "give",
"private": true,
"devDependencies": {
"gulp": "^3.9.1",
"gulp-sort": "^2.0.0",
"gulp-wp-pot": "^1.2.2",
"gulp-checktextdomain": "^1.0.2",
"fs-extra": "^0.30"
"name": "give-activecampaign",
"scripts": {
"dev": "npm run build",
"watch": "wp-scripts start src/FormExtension/FormBuilder src/FormExtension/DonationForm",
"build": "wp-scripts build src/FormExtension/FormBuilder src/FormExtension/DonationForm"
},
"dependencies": {
"natives": "^1.1.6"
"devDependencies": {
"@babel/eslint-parser": "^7.22.15",
"@babel/preset-typescript": "^7.22.15",
"@prettier/plugin-php": "^0.16.2",
"@stylelint/prettier-config": "^2.0.0",
"@types/react": "^18.2.21",
"@types/wordpress__blocks": "^12.5.3",
"@wordpress/eslint-plugin": "^9.0.1",
"@wordpress/scripts": "^26.12.0",
"@wordpress/stylelint-config": "^19.0.1"
},
"scripts": {
"preinstall": "npx npm-force-resolutions"
"engines": {
"node": ">=15.12.0"
},
"resolutions": {
"graceful-fs": "4.2.3"
"dependencies": {
"@givewp/form-builder-library": "^1.4.0",
"@wordpress/block-editor": "^12.9.0",
"@wordpress/blocks": "^12.18.0",
"@wordpress/element": "^5.18.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-select": "^5.8.0"
}
}
5 changes: 5 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
'autoprefixer': {}
}
};
74 changes: 74 additions & 0 deletions src/FormExtension/Actions/AddBlockToNewForms.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace GiveActiveCampaign\FormExtension\Actions;

use Give\DonationForms\Models\DonationForm;
use Give\Framework\Blocks\BlockModel;

/**
* @unreleased
*/
class AddBlockToNewForms
{
/**
* @unreleased
*/
public function __invoke(DonationForm $form)
{
$activeCampaign = give(\ActiveCampaign::class);

if (!$this->isEnabledGlobally() || !$activeCampaign->credentials_test()) {
return;
}

$form->blocks->insertAfter('givewp/email', BlockModel::make([
'name' => 'give-activecampaign/activecampaign',
'attributes' => [
'label' => $this->getLabel(),
'defaultChecked' => $this->getDefaultChecked(),
'selectedLists' => $this->getSelectedLists(),
'selectedTags' => $this->getSelectedTags(),
],
]));
}

/**
* @unreleased
*/
public function isEnabledGlobally(): bool
{
return give_is_setting_enabled(give_get_option( 'give_activecampaign_globally_enabled'));
}

/**
* @unreleased
*/
public function getLabel(): string
{
return give_get_option('give_activecampaign_label');
}

/**
* @unreleased
*/
protected function getDefaultChecked()
{
return give_is_setting_enabled(give_get_option('give_activecampaign_checkbox_default'));
}

/**
* @unreleased
*/
protected function getSelectedLists()
{
return give_get_option('give_activecampaign_lists', []);
}

/**
* @unreleased
*/
protected function getSelectedTags()
{
return give_get_option('give_activecampaign_tags');
}
}
46 changes: 46 additions & 0 deletions src/FormExtension/Actions/EnqueueDonationFormScripts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace GiveActiveCampaign\FormExtension\Actions;

/**
* @unreleased
*/
class EnqueueDonationFormScripts
{
/**
* @unreleased
* @var string
*/
protected $styleSrc;

/**
* @unreleased
* @var string
*/
protected $scriptSrc;

/**
* @unreleased
* @var array
*/
protected $scriptAsset;

/**
* @unreleased
*/
public function __construct()
{
$this->styleSrc = GIVE_ACTIVECAMPAIGN_URL . 'build/DonationForm.css';
$this->scriptSrc = GIVE_ACTIVECAMPAIGN_URL . 'build/DonationForm.js';
$this->scriptAsset = require GIVE_ACTIVECAMPAIGN_DIR . 'build/DonationForm.asset.php';
}

/**
* @unreleased
*/
public function __invoke()
{
wp_enqueue_script('givewp-form-extension-activecampaign', $this->scriptSrc, $this->scriptAsset['dependencies'], false, true);
wp_enqueue_style('givewp-form-extension-activecampaign', $this->styleSrc);
}
}
92 changes: 92 additions & 0 deletions src/FormExtension/Actions/EnqueueFormBuilderScripts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace GiveActiveCampaign\FormExtension\Actions;

class EnqueueFormBuilderScripts
{
/**
* @var \ActiveCampaign
*/
protected $activecampaign;

/**
* @unreleased
* @var string
*/
protected $styleSrc;

/**
* @unreleased
* @var string
*/
protected $scriptSrc;

/**
* @unreleased
* @var array
*/
protected $scriptAsset;

/**
* @unreleased
*/
public function __construct(\ActiveCampaign $activecampaign)
{
$this->activecampaign = $activecampaign;
$this->styleSrc = GIVE_ACTIVECAMPAIGN_URL . 'build/FormBuilder.css';
$this->scriptSrc = GIVE_ACTIVECAMPAIGN_URL . 'build/FormBuilder.js';
$this->scriptAsset = require GIVE_ACTIVECAMPAIGN_DIR . 'build/FormBuilder.asset.php';
}

/**
* @unreleased
*/
public function __invoke()
{
wp_enqueue_script('givewp-form-extension-activecampaign', $this->scriptSrc, $this->scriptAsset['dependencies'], false, true);
wp_localize_script('givewp-form-extension-activecampaign', 'GiveActiveCampaign', [
'requiresSetup' => ! $this->activecampaign->credentials_test(),
'settingsUrl' => admin_url('edit.php?post_type=give_forms&page=give-settings&tab=activecampaign'),
'lists' => $this->getLists(),
'tags' => $this->getTags(),
]);

wp_enqueue_style('givewp-form-extension-active-campaign', $this->styleSrc);
}

/**
* @unreleased
*/
protected function getLists(): array
{
if (!$this->activecampaign->credentials_test()) {
return [];
}

$lists = (array)$this->activecampaign->api('list/list', ['ids' => 'all']);

$lists = array_filter($lists, function ($list) {
return $list->name;
});

return array_map(function ($list) {
return ['id' => $list->id, 'name' => $list->name];
}, $lists);
}

/**
* @unreleased
*/
protected function getTags(): array
{
if (!$this->activecampaign->credentials_test()) {
return [];
}

$tags = json_decode($this->activecampaign->api('tags/list', ['ids' => 'all']));

return array_map(function ($tag) {
return ['value' => $tag->name, 'label' => $tag->name];
}, $tags);
}
}
59 changes: 59 additions & 0 deletions src/FormExtension/Actions/RenderDonationFormBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace GiveActiveCampaign\FormExtension\Actions;

use Give\Donations\Models\Donation;
use Give\Framework\Blocks\BlockModel;
use Give\Framework\FieldsAPI\Contracts\Node;
use Give\Framework\FieldsAPI\Exceptions\EmptyNameException;
use GiveActiveCampaign\FormExtension\DonationForm\Fields\ActiveCampaignField;

class RenderDonationFormBlock
{
/**
* Renders the ConvertKit field for the donation form block.
*
* @param Node|null $node The node instance.
* @param BlockModel $block The block model instance.
* @param int $blockIndex The index of the block.
*
* @return ActiveCampaignField
* @throws EmptyNameException
*/
public function __invoke($node, BlockModel $block, int $blockIndex): ?ActiveCampaignField
{
$activeCampaign = give(\ActiveCampaign::class);

if (!$activeCampaign->credentials_test()) {
return null;
}

return ActiveCampaignField::make('activecampaign')
->label((string)$block->getAttribute('label'))
->checked((bool)$block->getAttribute('defaultChecked'))
->selectedLists((array)$block->getAttribute('selectedLists'))
->selectedTags((array)$block->getAttribute('selectedTags'))
->scope(function (ActiveCampaignField $field, $value, Donation $donation) {
// If the field is checked, subscribe the donor to the list.
if (filter_var($value, FILTER_VALIDATE_BOOLEAN)) {
$subscriber = [
"email" => $donation->donor->email,
"first_name" => $donation->donor->firstName,
"last_name" => $donation->donor->lastName,
"tags" => implode(', ', $field->getSelectedTags()),
];

foreach ($field->getSelectedLists() as $list) {
$subscriber["p[$list]"] = $list;
}

$response = give(\ActiveCampaign::class)->api("contact/add", $subscriber);

// Add meta to the donation post that this donation opted-in to ActiveCampaign.
if ( ! empty($field->getSelectedTags()) || ! empty($field->getSelectedLists())) {
add_post_meta($donation->id, '_give_activecampaign_donation_optin_status', 'true');
}
}
});
}
}
57 changes: 57 additions & 0 deletions src/FormExtension/DonationForm/Fields/ActiveCampaignField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace GiveActiveCampaign\FormExtension\DonationForm\Fields;

use Give\Framework\FieldsAPI\Checkbox;

/**
* @unreleased
*/
class ActiveCampaignField extends Checkbox
{
/**
* @unreleased
*/
protected $selectedLists = [];

/**
* @unreleased
*/
protected $selectedTags = [];

public const TYPE = 'activecampaign';

/**
* @unreleased
*/
public function selectedLists(array $selectedLists): ActiveCampaignField
{
$this->selectedLists = $selectedLists;
return $this;
}

/**
* @unreleased
*/
public function getSelectedLists(): array
{
return $this->selectedLists;
}
/**
* @unreleased
*/
public function selectedTags(array $selectedTags): ActiveCampaignField
{
$this->selectedTags = $selectedTags;
return $this;
}

/**
* @unreleased
*/
public function getSelectedTags(): array
{
return $this->selectedTags;
}

}
4 changes: 4 additions & 0 deletions src/FormExtension/DonationForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import FieldTemplate from './template';

// @ts-ignore
window.givewp.form.templates.fields.activecampaign = FieldTemplate;
37 changes: 37 additions & 0 deletions src/FormExtension/DonationForm/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.givewp-activecampaign-field {
border: 1px solid var(--givewp-primary-color);
border-radius: 5px;
padding: var(--givewp-spacing-4) var(--givewp-spacing-6);

&.invalid {
border-color: red;
}

p {
font-size: 1rem;
line-height: 1.5;
margin: 0;
}

label {
align-items: center;
cursor: pointer;
display: flex;
gap: 1rem;
margin: 0;
font-weight: normal;

input[type=checkbox] {
width: 1.5rem;
height: 1.5rem;
flex: 0 0 auto;
margin: 0;
border-color: var(--givewp-primary-color);
}

.error-message {
flex: 1 0 100%;
}
}
}

22 changes: 22 additions & 0 deletions src/FormExtension/DonationForm/template.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "./styles.scss";

// @ts-ignore
const { checkbox: Checkbox } = window.givewp.form.templates.fields;

/**
* @unreleased
*/
export default function FieldTemplate({ErrorMessage, label, checked, inputProps}) {
return (
<div className={'givewp-activecampaign-field'}>
<Checkbox
Label={() => label}
ErrorMessage={ErrorMessage}
inputProps={{
defaultChecked: checked,
...inputProps,
}}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { BaseControl, CheckboxControl } from "@wordpress/components";
import { __ } from "@wordpress/i18n";
import { Fragment, useEffect } from "react";
import { lists } from "../../window";

import "./styles.scss";

type ListControlProps = {
id: string;
onChange: (values: string[]) => void;
lists: lists[];
selectedLists: selectedLists;
};

type selectedLists = string[];

export default function ListsControl({
id,
onChange,
lists,
selectedLists,
}: ListControlProps) {
useEffect(() => {
if (selectedLists.length === 0 && lists.length > 0) {
const minListsRequired = [lists[0].id];
onChange(minListsRequired);
}
}, [selectedLists, lists]);

const handleListSelection = (isChecked: boolean, id: string) => {
let updatedList: string[];
if (isChecked) {
updatedList = [...selectedLists, id];
} else {
updatedList = selectedLists.filter((list) => list !== id);
}

// Ensure at least one checkbox is checked
if (updatedList.length === 0 && lists.length > 0) {
updatedList.push(lists[0].id);
}

onChange(updatedList);
};

return (
<BaseControl
id={id}
className={"givewp-activecampaign-lists-control"}
help={
lists
? __(
"Customize the list(s) you wish donors to subscribe to if they opt-in.",
"give-activecampaign"
)
: __(
"We were unable to find any email list's for your account. Please visit Constant Contact and verify you have created at least one mailing list to use this block.",
"give-activecampaign"
)
}
label={__("List opt-in", "give-activecampaign")}
>
{lists &&
lists.map(({ id, name }) => (
<Fragment key={id}>
<ListCheckboxControl
id={id}
name={name}
checked={selectedLists.includes(id)}
handleListSelection={handleListSelection}
/>
</Fragment>
))}
</BaseControl>
);
}

type ListCheckboxProps = {
id: string;
name: string;
checked: boolean;
handleListSelection: (isChecked: boolean, id: string) => void;
};

function ListCheckboxControl({
name,
id,
checked,
handleListSelection,
}: ListCheckboxProps) {
const handleChange = () => {
handleListSelection(!checked, id);
};

return (
<CheckboxControl
checked={checked}
id={id}
label={name}
onChange={handleChange}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#givewp-activecampaign-lists-control {
&__label {
display: inline-block;
padding: 0;
font-weight: 500;
line-height: 1.4;
text-transform: uppercase;
color: inherit;
}

&__help {
margin: 1rem 0 0 0;
}

&__groups {
display: flex;
align-items: center;
margin: 0;

.components-base-control__field {
margin: 0;

.components-checkbox-control__input-container {
margin-left: 2rem;
}
}
}

.block-editor-block-inspector, .components-base-control {
margin: .5rem 0 0 0 !important;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { BaseControl } from "@wordpress/components";
import { __ } from "@wordpress/i18n";
import { useEffect, useState } from "react";
import ReactSelect from "react-select";
import { tag } from "../../window";

import "./styles.scss";

type TagControlProps = {
id: string;
label: string;
help: string;
tagOptions: tag[];
selectedTags: string[];
onChange: (tag: string[]) => void;
};

/**
* @unrleased
*/
export default function TagControls({
id,
label,
help,
tagOptions,
selectedTags,
onChange,
}: TagControlProps) {
const [filteredValues, setFilteredValues] = useState<tag[]>([]);

useEffect(() => {
setFilteredValues(handleDefaultValues());
}, [selectedTags, tagOptions]);

const handleDefaultValues = () => {
if (selectedTags) {
return tagOptions.filter(({ value, label }) =>
selectedTags?.includes(value || label)
);
}
return [];
};

const handleChange = (tags: tag[]) => {
const newTags = tags.map(({ value }) => value);
onChange(newTags);
};

return (
<BaseControl
id={id}
className={"givewp-activecampaign-tag-controls"}
label={label}
help={help}
__nextHasNoMarginBottom={true}
>
<ReactSelect
isMulti
name={"subscriptionTagListControl"}
placeholder={__(
"Add subscription tags to this form",
"give-activecampaign"
)}
value={filteredValues}
options={tagOptions}
onChange={handleChange}
/>
</BaseControl>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.givewp-activecampaign-tag-controls {
cursor: pointer;

input[type="text"]:focus {
border-color: transparent;
box-shadow: 0 0 0 1px transparent;
outline: 2px solid transparent;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { PanelBody, TextControl, ToggleControl } from "@wordpress/components";
import { __ } from "@wordpress/i18n";
import { BlockNotice } from "@givewp/form-builder-library";
import { InspectorControls } from "@wordpress/block-editor";
import { getWindowData } from "../window";
import TagControls from "./TagsControl";
import ListsControl from "./ListsControl";

export default function BlockInspectorControls({ attributes, setAttributes }) {
const { selectedLists, selectedTags, defaultChecked, label } = attributes;

const { settingsUrl, requiresSetup } = getWindowData();
const { lists, tags } = getWindowData();

return (
<InspectorControls>
<PanelBody
title={__("Field Settings", "give-activecampaign")}
initialOpen={true}
>
{requiresSetup ? (
<BlockNotice
title={__("ActiveCampaign requires setup", "give-activecampaign")}
description={__(
"This block requires your settings to be configured in order to use.",
"give-activecampaign"
)}
anchorText={__(
"Connect your ActiveCampaign account",
"give-activecampaign"
)}
href={settingsUrl}
/>
) : (
<div className={"givewp-activecampaign-controls"}>
<TextControl
label={__("Custom Label", "give-activecampaign")}
value={label}
help={__(
"Customize the label for the ActiveCampaign opt-in checkbox",
"give-activecampaign"
)}
onChange={(value) => setAttributes({ label: value })}
/>

<ToggleControl
label={__("Opt-in Default", "give-activecampaign")}
checked={defaultChecked}
onChange={() =>
setAttributes({ defaultChecked: !defaultChecked })
}
help={__(
"Customize the newsletter opt-in option for this form.",
"give-activecampaign"
)}
/>

<ListsControl
id={"givewp-activecampaign-tag-controls"}
onChange={(values) => setAttributes({ selectedLists: values })}
lists={lists}
selectedLists={selectedLists}
/>

<TagControls
id={"givewp-activecampaign-controls-tags"}
help={__(
"These tags will be applied to Subscribers based on the form they used to sign up.",
"give-activecampaign"
)}
label={__("Subscriber Tags", "give-activecampaign")}
onChange={(tag) => setAttributes({ selectedTags: tag })}
tagOptions={tags}
selectedTags={selectedTags}
/>
</div>
)}
</PanelBody>
</InspectorControls>
);
}
40 changes: 40 additions & 0 deletions src/FormExtension/FormBuilder/Block/BlockPlaceholder/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { CheckboxControl } from "@wordpress/components";
import { __ } from "@wordpress/i18n";
import { getWindowData } from "../window";
import { createInterpolateElement } from "@wordpress/element";
import "./styles.scss";

/**
* @unreleased
*/
export default function BlockPlaceholder({ defaultChecked, label }) {
const { requiresSetup, settingsUrl } = getWindowData();

return (
<div
className={`givewp-active-campaign-block-placeholder
${requiresSetup && "givewp-active-campaign-block-placeholder--invalid"}`}
>
{requiresSetup ? (
createInterpolateElement(
__(
"This block requires additional setup. Go to your <a>Settings</a> to connect your ActiveCampaign account.",
"give"
),
{
a: (
<a href={settingsUrl} target="_blank" rel="noopener noreferrer" />
),
}
)
) : (
<CheckboxControl
checked={defaultChecked}
label={label}
onChange={null}
disabled={true}
/>
)}
</div>
);
}
28 changes: 28 additions & 0 deletions src/FormExtension/FormBuilder/Block/BlockPlaceholder/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.givewp-active-campaign-block-placeholder {
background-color: var(--givewp-gray-10);
border: 1px solid var(--wp-admin-theme-color);
border-radius: 5px;
padding: var(--givewp-spacing-3) var(--givewp-spacing-6);

label,
.label {
color: var(--givewp-gray-900);
font-size: 1rem;
font-weight: 500;
line-height: 1.5;
}

&--invalid {
background: none;
border: 1px dashed var(--givewp-grey-700);
border-radius: 0.3125rem;
padding: var(--givewp-spacing-6) var(--givewp-spacing-4);
font-size: 14px;

a {
color: var(--givewp-grey-900);
font-weight: 600;
cursor: pointer
}
}
}
20 changes: 20 additions & 0 deletions src/FormExtension/FormBuilder/Block/edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {BlockEditProps} from "@wordpress/blocks";
import BlockPlaceholder from "./BlockPlaceholder";
import BlockInspectorControls from "./BlockInspectorControls";

/**
* @unreleased
*/
export default function Edit({
attributes,
setAttributes,
}: BlockEditProps<any>) {
const { defaultChecked, label } = attributes;

return (
<>
<BlockPlaceholder {...{ defaultChecked, label }} />
<BlockInspectorControls {...{ attributes, setAttributes }} />
</>
);
}
15 changes: 15 additions & 0 deletions src/FormExtension/FormBuilder/Block/icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Path, SVG} from '@wordpress/components';

/**
* ActiveCampaign "Blue Mark"
* @link https://www.activecampaign.com/about/newsroom/branding
* @unreleased
*/
const Icon = (
<SVG fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="121.47 91.37 83.78 123.27">
<Path d="M190.319 152.752L125.953 195.497C122.971 197.485 121.479 200.715 121.479 203.946V214.632L199.514 163.438C202.993 160.953 205.23 156.976 205.23 152.752C205.23 148.527 203.242 144.55 199.514 142.065L121.479 91.3677V101.308C121.479 104.788 123.219 108.018 125.953 109.758L190.319 152.752Z" fill="#004CFF"/>
<Path d="M151.55 156.231C155.029 158.467 159.503 158.467 162.982 156.231L168.449 152.503L127.692 124.918C125.207 123.178 121.479 124.918 121.479 128.148V136.349L142.603 150.515L151.55 156.231Z" fill="#004CFF"/>
</SVG>
);

export default Icon;
20 changes: 20 additions & 0 deletions src/FormExtension/FormBuilder/Block/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {BlockConfiguration} from '@wordpress/blocks';
import metadata from './metadata';
import Icon from './icon';
import edit from './edit';

const {name} = metadata;

const settings = {
...metadata,
icon: Icon,
edit,
save: () => null,
};

/**
* @unreleased
*/
const block: {name: string; settings: BlockConfiguration} = {name, settings};

export default block;
38 changes: 38 additions & 0 deletions src/FormExtension/FormBuilder/Block/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {BlockConfiguration} from '@wordpress/blocks';
import {__} from '@wordpress/i18n';

/**
* @unreleased
*/
const metadata: BlockConfiguration = {
name: 'give-activecampaign/activecampaign',
title: __('Active Campaign', 'give-activecampaign'), // Note: The brand is "ActiveCampaign" (one word), but in this context the word break is more legible.
description: __(
'Easily integrate ActiveCampaign opt-ins within your Give donation forms.',
'give-activecampaign'
),
category: 'addons',
supports: {
multiple: false,
},
attributes: {
label: {
type: 'string',
default: __('Subscribe to our newsletter?', 'give'),
},
defaultChecked: {
type: 'boolean',
default: true,
},
selectedLists: {
type: 'array',
default: [],
},
selectedTags: {
type: 'array',
default: [],
},
},
};

export default metadata;
28 changes: 28 additions & 0 deletions src/FormExtension/FormBuilder/Block/window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @unreleased
*/

type windowData = {
requiresSetup: boolean;
settingsUrl: string;
lists: lists[];
tags: tag[];
};

declare const window: {
GiveActiveCampaign: windowData;
} & Window;

export function getWindowData(): windowData {
return window.GiveActiveCampaign;
}

export type tag = {
value: string;
label: string;
};

export type lists = {
id: string;
name: string;
};
4 changes: 4 additions & 0 deletions src/FormExtension/FormBuilder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import block from './Block';

// @ts-ignore
window.givewp.form.blocks.register(block.name, block.settings);
45 changes: 45 additions & 0 deletions src/FormExtension/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace GiveActiveCampaign\FormExtension;

use Give\Helpers\Hooks;
use Give\ServiceProviders\ServiceProvider as ServiceProviderInterface;

/**
* @unreleased
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* @unreleased
* @inheritDoc
*/
public function register(): void
{
give()->singleton(\ActiveCampaign::class, static function () {
return new \ActiveCampaign(
$api_url = give_get_option( 'give_activecampaign_apiurl', false ),
$api_key = give_get_option( 'give_activecampaign_api', false )
);
});
}

/**
* @unreleased
* @inheritDoc
*/
public function boot(): void
{
Hooks::addAction('givewp_form_builder_new_form', Actions\AddBlockToNewForms::class);
Hooks::addAction('givewp_form_builder_enqueue_scripts', Actions\EnqueueFormBuilderScripts::class);
Hooks::addAction('givewp_donation_form_enqueue_scripts', Actions\EnqueueDonationFormScripts::class);

Hooks::addFilter(
'givewp_donation_form_block_render_give-activecampaign/activecampaign',
Actions\RenderDonationFormBlock::class,
'__invoke',
10,
4
);
}
}
26 changes: 26 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": [
"esnext",
"dom"
],
"jsx": "react-jsx",
"moduleResolution": "node",
"allowJs": true,
"strict": false,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"plugins": [
{
"name": "typescript-plugin-css-modules"
}
]
},
"include": [
"./src",
"./globals.d.ts"
]
}

0 comments on commit 3be6c99

Please sign in to comment.